Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public interface IMembaseApi
Task<PgtValidationResponse> ValidatePgtDefinitionAsync(string graphId, string definitionId, [Body] PgtValidationRequest request);

[Post("/graph/{graphId}/pgt-external/{correlationId}/complete")]
Task<PgtExternalCompleteResponse> CompletePgtExternalAsync(string graphId, string correlationId, [FromBody] object emptyBody);
Task<PgtExternalCompleteResponse> CompletePgtExternalAsync(string graphId, string correlationId, [FromBody] object body);
#endregion

#region Procedure
Expand Down
6 changes: 5 additions & 1 deletion src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)

services.AddHttpContextAccessor();
services.AddTransient<MembaseAuthHandler>();
services.AddRefitClient<IMembaseApi>(new RefitSettings
// Use plain Web options WITHOUT Refit's default ObjectToInferredTypesConverter:
// consumers (ObjectExtensions.TryGetValue<JsonElement>, GraphBuilder, GetNodePropertiesOrDefault)
// rely on Dictionary<string, object?> values deserializing as JsonElement.
services.AddRefitClient<IMembaseApi>(new RefitSettings(
new SystemTextJsonContentSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)))
{
CollectionFormat = CollectionFormat.Multi
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public static OpenAIClient GetClient(string provider, string model, string? apiK
{
var settingsService = services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting(provider, model);
if (settings == null && string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException($"No LLM model settings found for '{provider}.{model}'. Register the model under LlmProviders (appsettings/user secrets) or pass an api key.");
}
Comment on lines +12 to +15

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.

Remediation recommended

1. Whitespace api key bypass 🐞 Bug ≡ Correctness

ProviderHelper.GetClient checks string.IsNullOrEmpty(apiKey), so a whitespace-only apiKey bypasses
the new missing-settings guard and is still used to construct ApiKeyCredential. This can override a
valid configured settings.ApiKey and cause late authentication failures instead of the intended
clear configuration error.
Agent Prompt
### Issue description
`ProviderHelper.GetClient` currently treats whitespace-only `apiKey` values as present because it uses `string.IsNullOrEmpty(apiKey)`. This allows invalid keys (e.g., `"   "`) to bypass the new guard and also overrides a valid configured `settings.ApiKey`, producing confusing downstream authentication failures.

### Issue Context
This was introduced with the new early validation logic added to `GetClient`.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.OpenAI/Providers/ProviderHelper.cs[8-19]

### Suggested fix
- Normalize the candidate key and validate the *effective* key:
  - Use `string.IsNullOrWhiteSpace(apiKey)` (and optionally `apiKey = apiKey?.Trim()`)
  - Select `effectiveApiKey = !string.IsNullOrWhiteSpace(apiKey) ? apiKey.Trim() : settings?.ApiKey`
  - Throw if `string.IsNullOrWhiteSpace(effectiveApiKey)`
  - Construct `ApiKeyCredential(effectiveApiKey)`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

var options = !string.IsNullOrEmpty(settings?.Endpoint) ?
new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) } : null;
return new OpenAIClient(new ApiKeyCredential(apiKey ?? settings!.ApiKey), options);
Expand Down
Loading