google-gemini / google-gemini/gemini-cli
security(a2a-server): HTTP API never enforces authentication, and the only auth check compares against hardcoded public credentials ('valid-token', 'admin:password')
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
The `@google/gemini-cli-a2a-server` HTTP server (published to npm with the `gemini-cli-a2a-server` bin) does not actually enforce authentication on any endpoint, even though its Agent Card advertises bearer/basic security schemes. Additionally, the only authentication logic that exists uses **hardcoded public demo credentials** checked against values committed to this public repository.
`packages/a2a-server/src/http/app.ts` (main):
```ts
const customUserBuilder: UserBuilder = async (req: Request) => {
const auth = req.headers['authorization'];
...
// 1. Bearer Auth
if (auth.startsWith('Bearer ')) {
const token = auth.substring(7);
if (token === 'valid-token') { // <- hardcoded public token
return { userName: 'bearer-user', isAuthenticated: true };
}
}
// 2. Basic Auth
if (auth.startsWith('Basic ')) {
const credentials = Buffer.from(auth.substring(6), 'base64').toString();
if (credentials === 'admin:password') { // <- hardcoded public creds
return { userName: 'basic-user', isAuthenticated: true };
}
}
return new UnauthenticatedUser();
};
```
Two problems:
1. **Authentication is never enforced.** The `userBuilder` result is only attached to the A2A call context by `@a2a-js/sdk` (`jsonRpcHandler` builds `context = ctxBuilder({ ..., user })`); nothing in `packages/a2a-server` ever rejects a request when `isAuthenticated === false`. Verified by:
- `app.test.ts` / `endpoints.test.ts` successfully exercise `/executeCommand`, `/tasks`, `/tasks/metadata` etc. with **no Authorization header at all**; there is not a single 401 assertion in the package.
- `grep isAuthenticated packages/a2a-server/src` only shows where it is *set* to true, never checked.
2. **Even if enforcement were added, the credentials are public constants** (`'valid-token'`, `'admin:password'`) known to anyone who can read this repo, so they provide zero security value as implemented.
Impact: any local process that can reach `http://localhost:/` can invoke the full agent surface unauthenticated — `POST /executeCommand` (executes registered commands, including streaming command execution), task creation via JSON-RPC (`/tasks`), etc., driving an agent with workspace access. The localhost-only bind (`expressApp.listen(port, 'localhost', ...)` at app.ts:411) limits exposure to local processes and container/host-boundary confusion scenarios (e.g., `host.docker.internal` mapping in `getIdeServerHost()`-style setups), but "any code on the machine can drive the agent" still violates the trust boundary the advertised `securitySchemes` imply.
### What did you expect to happen?
Either:
- Enforce authentication in middleware before all routes (reject 401 when `!user.isAuthenticated`), and source credentials from configuration/environment with no defaults; or
- If the server is intended to be unauthenticated-by-design for local dev, remove the misleading `securitySchemes` from `coderAgentCard` and delete the hardcoded credential check so downstream users don't assume protection that doesn't exist.
### Client information
Source-level finding verified against upstream `main` at commit `5411f113c`; cross-checked the `@a2a-js/sdk@1.0.1` express handler behavior (`user` is attached to context, never rejected).
Platform: applies to all platforms; affects the published `@google/gemini-cli-a2a-server` package.
### Login information
Not applicable.
### Anything else we need to know?
Sources:
- `packages/a2a-server/src/http/app.ts:96-122` — `customUserBuilder` with hardcoded `'valid-token'` / `'admin:password'`
- `packages/a2a-server/src/http/app.ts:411` — localhost-only listener (exposure bound)
- `packages/a2a-server/src/http/app.ts:52-56` — Agent Card advertises `bearerAuth`/`basicAuth` `securitySchemes` + `security` requirement
- `node_modules/@a2a-js/sdk/dist/server/express/index.js` (`jsonRpcHandler`) — user attached to call context without rejection
- `packages/a2a-server/src/http/app.test.ts` / `endpoints.test.ts` — endpoints exercised without auth headers; no 401 tests
- Duplicate check: searched issues/PRs for "a2a authentication", "valid-token", "a2a auth", "hardcoded" — no existing report or fix found.
Contributor guide
Research direction
Start with packages/a2a-server/src/http/app.ts, especially customUserBuilder, the Agent Card security configuration, and the listener setup. Read app.test.ts and endpoints.test.ts alongside the SDK jsonRpcHandler behavior to establish the current request flow. Confirm whether authentication should be enforced or removed, then make the tests demonstrate the chosen behavior for unauthenticated requests and configured credentials.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend-api-design, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100