- Location
- backend/src/services.ts:90
- Finding
- Model API Keys and Channel Secrets Disclosed in Plaintext<![CDATA[
## Vulnerability Details
**File Location**: `backend/src/services.ts:90-115, 193-210, 366-369, 438-443`; `backend/src/app.ts:163-165, 190-192`
**Vulnerability Type**: Plaintext secret disclosure through API responses
**Risk Level**: Critical
### Vulnerable Code
```ts
const providerApiKey = toText(providerObj.apiKey, "");
const providerBaseUrl = toText(providerObj.baseUrl, "");
const providerModels = Array.isArray(providerObj.models) ? providerObj.models : [];
for (const item of providerModels) {
const modelObj = pickObject(item);
const modelId = toText(modelObj.id, "");
if (!modelId) {
continue;
}
const nextModel: ModelConfig = {
id: modelId,
modelId,
providerId,
platform,
name: toText(modelObj.name, modelId),
enabled: providerEnabled && toBool(modelObj.enabled, true),
maxTokens: toNumber(modelObj.maxTokens, toNumber(modelObj.contextWindow, 8192)),
apiKey: providerApiKey,
baseUrl: providerBaseUrl
};
result.set(modelId, nextModel);
}
```
```ts
const robotSecret = toText(
channelObj.appSecret,
toText(defaultAccount.appSecret, "")
);
result.push({
id,
name: toText(channelObj.name, id === "feishu" ? "Feishu channel" : id),
enabled: toBool(channelObj.enabled, true),
weight: toNumber(channelObj.weight, id === "feishu" ? 200 : 100),
robotId,
robotSecret
});
```
```ts
app.get("/api/models", (_req, res) => {
res.json(listModels());
});
app.get("/api/channels", (_req, res) => {
res.json(listChannels());
});
```
### Technical Analysis
The gateway reads model-provider API keys and channel application secrets from the OpenClaw runtime configuration and places the complete values in `ModelConfig` and `ChannelConfig` objects. The corresponding GET endpoints serialize those objects directly.
No masking, response DTO, field omission, or authorization check protects these values. Consequently, possession of network access to the gateway is sufficient to retrieve credentials intended for
...[truncated 833 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
1. Never include secret values in model or channel read responses.
2. Define dedicated response types that omit `apiKey`, `robotSecret`, tokens, and authorization-header values.
3. Return only metadata such as:
```json
{
"apiKeyConfigured": true,
"apiKeyHint": "****abcd"
}
```
4. Treat an omitted secret on update as “leave unchanged”; require a separate, privileged rotation operation to replace it.
5. Require elevated authorization and recent re-authentication for secret rotation.
6. Store secrets in an operating-system credential facility or dedicated secret manager instead of general JSON configuration.
7. Rotate all credentials that may already have been exposed through these endpoints.
8. Add automated response-schema tests that fail if secret fields are serialized.
]]>