Skip to content

fix: inject headers into service params#277

Merged
nahapetyan-serob merged 4 commits intomainfrom
serob/rest-inject-headers-into-service-params
Mar 11, 2026
Merged

fix: inject headers into service params#277
nahapetyan-serob merged 4 commits intomainfrom
serob/rest-inject-headers-into-service-params

Conversation

@nahapetyan-serob
Copy link
Copy Markdown
Collaborator

fixes #275

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the "a2asrv" REST handlers by integrating HTTP request headers directly into the "context.Context" for each incoming request. This change ensures that critical information, such as authorization tokens, is readily available to downstream service logic and interceptors, addressing a previously identified issue with header propagation.

Highlights

  • Contextual Header Injection: Modified all REST handler functions to utilize a new "withCallContext" helper method, ensuring that HTTP request headers are consistently injected into the "context.Context" as "ServiceParams".
  • New withCallContext Method: Introduced a private "withCallContext" method within "restHandler" to centralize the creation of a "context.Context" that includes "ServiceParams" derived from the incoming HTTP request headers.
  • Service Parameter Test Coverage: Added a new unit test, "TestREST_ServiceParams", to validate the correct propagation and retrieval of HTTP headers (specifically "Authorization") within the "CallContext"'s "ServiceParams".
Changelog
  • a2asrv/rest.go
    • Replaced direct "req.Context()" calls with "h.withCallContext(req)" in "handleSendMessage", "handleStreamMessage", "handleGetTask", "handleListTasks", "handlePOSTTasks", "handleCancelTask", "handleStreamingRequest", "handleCreateTaskPushConfig", "handleGetTaskPushConfig", "handleListTaskPushConfigs", "handleDeleteTaskPushConfig", and "handleGetExtendedAgentCard".
    • Added "withCallContext" method to create a new context with "ServiceParams" initialized from "req.Header".
  • a2asrv/rest_test.go
    • Added "TestREST_ServiceParams" to verify that request headers are correctly accessible via "CallContext.ServiceParams()".
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request injects HTTP headers into service parameters for REST handlers, allowing interceptors to access them via a new withCallContext helper function. However, this introduces a significant security risk by potentially leaking sensitive headers like Authorization and Cookie to untrusted agents, which contradicts the guideline that authorization should be handled at the interceptor level. Additionally, the current implementation of withCallContext unconditionally creates a new CallContext, which shadows any existing context and breaks authentication performed by upstream middleware. It is recommended to refactor this to use an HTTP middleware to centralize context creation, reduce code duplication, and improve maintainability, while also ensuring sensitive headers are filtered and existing context information is preserved.

a2asrv/rest.go Outdated
}

func (h *restHandler) withCallContext(req *http.Request) context.Context {
ctx, _ := NewCallContext(req.Context(), NewServiceParams(req.Header))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

This implementation introduces two security concerns:

  1. Sensitive Information Leakage: All HTTP headers from the incoming request are injected into ServiceParams. These parameters are passed to the AgentExecutor and can be forwarded by the a2aclient to other services. This results in the leakage of sensitive headers like Authorization and Cookie to potentially untrusted agents or third-party services.

  2. Broken Access Control (Authentication Shadowing): Unconditionally calling NewCallContext creates a new CallContext that shadows any existing one in the request context. This resets the User information to an unauthenticated state (Authenticated: false), effectively breaking any authentication performed by middleware higher up in the http.Handler chain.

Remediation:

  • Filter the headers before injecting them into ServiceParams to exclude sensitive ones (e.g., Authorization, Cookie, Proxy-Authorization).
  • Modify withCallContext to check if a CallContext already exists in the context and preserve its information (especially the User) instead of unconditionally creating a new one.
References
  1. Authorization should be performed by SDK users at the interceptor level, implying that Authorization headers are sensitive and should not be leaked or mishandled.

a2asrv/rest.go Outdated

func (h *restHandler) handleSendMessage(rw http.ResponseWriter, req *http.Request) {
ctx := req.Context()
ctx := h.withCallContext(req)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This change is repeated across all handlers. To improve maintainability and avoid code duplication, consider using an HTTP middleware to inject the CallContext. This would centralize the logic and apply it to all routes handled by this mux.

You could define a middleware:

func withCallContextMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx, _ := NewCallContext(r.Context(), NewServiceParams(r.Header))
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

And then wrap the mux in NewRESTHandler:

// In NewRESTHandler, at the end
// ...
return withCallContextMiddleware(mux)

With this middleware in place, all the handle... functions can be reverted to use ctx := req.Context(), as the context from the request will already contain the ServiceParams. This would make the code cleaner and less prone to errors when adding new handlers.

Comment on lines +467 to +470
ctx := t.Context()
tid := a2a.NewTaskID()
task := &a2a.Task{ID: tid, ContextID: a2a.NewContextID()}
store := testutil.NewTestTaskStore().WithTasks(t, task)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this setup here is not really necessary, because we're checking the code which gets executed regardless of whether a task exists or not

@nahapetyan-serob nahapetyan-serob merged commit d33f3bd into main Mar 11, 2026
4 checks passed
@nahapetyan-serob nahapetyan-serob deleted the serob/rest-inject-headers-into-service-params branch March 11, 2026 15:48
yarolegovich pushed a commit that referenced this pull request Mar 17, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.0.0](v1.0.0-alpha.3...v1.0.0)
(2026-03-17)


### Features

* implement the new rest error handling
([#282](#282))
([a3bda30](a3bda30))
* use v2 suffix for module ID and provide compat support
([#270](#270))
([dd1b6ba](dd1b6ba)),
closes [#250](#250)


### Bug Fixes

* a2asrv jsonrpc Content-Type
([#265](#265))
([2568a46](2568a46))
* bugs before going from alpha
([#279](#279))
([b1f055c](b1f055c))
* GetTaskRequest nil pointer assignment check
([#258](#258))
([440bb79](440bb79))
* inject headers into service params
([#277](#277))
([d33f3bd](d33f3bd)),
closes [#275](#275)
* propagate cancelation signal using task store
([#272](#272))
([5e1d462](5e1d462)),
closes [#245](#245)
* regenerate spec and fix returnImmediately
([#284](#284))
([2eee0b9](2eee0b9))
* task modified after save
([#266](#266))
([c15febe](c15febe))
* taskupdater result mutable
([#274](#274))
([6038d92](6038d92))
* update pushsender
([#256](#256))
([5f7a594](5f7a594))
* use enum values as in the spec
([#261](#261))
([eb98981](eb98981)),
closes [#251](#251)


### Documentation

* **a2asrv:** add Example_* test functions for pkg.go.dev documentation
([#262](#262))
([7888e37](7888e37))
* add example tests a2a
([#240](#240))
([4fe08a9](4fe08a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: NewRESTHandler does not inject HTTP headers into CallContext ServiceParams

2 participants