Changed private web pages and the Confluence search to require a High-confidence provider

This commit is contained in:
Thorsten Sommer committed 2026-09-23 16:31:21 +02:00
1 parent 56978d88f2
commit ad6b285c8d
11 files changed
+34 -51

No files matched your search

@@ -28,9 +28,9 @@ public sealed class ConfluenceSearchTool(WebPageRetrievalService webPageRetrieva
{
Id = ToolSelectionRules.SEARCH_CONFLUENCE_TOOL_ID,
ImplementationKey = ToolSelectionRules.SEARCH_CONFLUENCE_TOOL_ID,
// Kept low so that providers trusted by the organization below HIGH are offered the tool.
// The runtime check in ExecuteAsync requires HIGH confidence or that trust.
MinimumProviderConfidence = ConfidenceLevel.VERY_LOW,
// Every search result is internal to the organization and raises the chat's required
// confidence to HIGH, so only providers which may continue the chat are offered the tool:
MinimumProviderConfidence = ConfidenceLevel.HIGH,
SettingsSchema = ToolSettingsSchemaBuilder.Create()
.Required(BASE_URL_SETTING)
.Optional(TIMEOUT_SECONDS_SETTING)
@@ -96,12 +96,12 @@ public sealed class ConfluenceSearchTool(WebPageRetrievalService webPageRetrieva
public async Task<ToolExecutionResult> ExecuteAsync(JsonElement arguments, ToolExecutionContext context, CancellationToken token = default)
{
//
// A provider trusted by the organization's configuration counts as much as a High-confidence
// one. The chat thread's own check does the same, so such a provider may also continue the
// chat after the result raised its required confidence to HIGH.
// The tool settings may lower the level at which the tool is offered, but what the wiki
// returns stays internal to the organization. The search itself therefore always needs
// a High-confidence provider.
//
if (context.ProviderConfidence < ConfidenceLevel.HIGH && !context.ProviderIsTrustedByConfiguration)
throw new ToolExecutionBlockedException(TB("Searching the company wiki requires a High-confidence provider or one trusted by your organization's configuration."));
if (context.ProviderConfidence < ConfidenceLevel.HIGH)
throw new ToolExecutionBlockedException(TB("Searching your company's wiki requires a High-confidence provider."));
if (!TryParseBaseUrl(context.SettingsValues.GetValueOrDefault(BASE_URL_SETTING), out var baseUrl))
throw new InvalidOperationException(TB("The Confluence base URL is not configured correctly."));
@@ -136,7 +136,6 @@ public sealed class ConfluenceSearchTool(WebPageRetrievalService webPageRetrieva
{
TimeoutSeconds = timeoutSeconds,
ProviderConfidence = context.ProviderConfidence,
ProviderIsTrustedByConfiguration = context.ProviderIsTrustedByConfiguration,
UseOsSso = true,
IsPrivateHostAllowed = host => IsWikiHost(baseUrl!, host),
@@ -73,7 +73,7 @@ public sealed class ReadWebPageTool(WebPageRetrievalService webPageRetrievalServ
{
TIMEOUT_SECONDS_SETTING => TB("(Optional) HTTP timeout for loading a web page in seconds."),
MAX_CONTENT_CHARACTERS_SETTING => TB("(Optional) Global truncation limit for extracted characters returned to the model."),
ALLOWED_PRIVATE_HOSTS_SETTING => TB("(Optional) Host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a High-confidence provider or a provider trusted by your organization's configuration. For allowed HTTPS internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication."),
ALLOWED_PRIVATE_HOSTS_SETTING => TB("(Optional) Host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a High-confidence provider. For allowed HTTPS internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication."),
_ => TB(fieldDefinition.Description),
};
@@ -144,7 +144,6 @@ public sealed class ReadWebPageTool(WebPageRetrievalService webPageRetrievalServ
{
TimeoutSeconds = timeoutSeconds,
ProviderConfidence = context.ProviderConfidence,
ProviderIsTrustedByConfiguration = context.ProviderIsTrustedByConfiguration,
UseOsSso = true,
IsPrivateHostAllowed = host => IsAllowedPrivateHost(host, allowedPrivateHosts),
OnPrivateHostProviderBlockAsync = this.ReportPrivateHostProviderBlockAsync,
@@ -275,13 +274,13 @@ public sealed class ReadWebPageTool(WebPageRetrievalService webPageRetrievalServ
private async Task ReportPrivateHostProviderBlockAsync(Uri url, ConfidenceLevel providerConfidence)
{
logger.LogWarning(
"Blocked read_web_page access to allowed private host '{Host}' because provider confidence '{ProviderConfidence}' is below HIGH and the provider is not trusted by configuration.",
"Blocked read_web_page access to allowed private host '{Host}' because provider confidence '{ProviderConfidence}' is below HIGH.",
url.Host,
providerConfidence);
await MessageBus.INSTANCE.SendError(new DataErrorMessage(
Icons.Material.Filled.Security,
TB("The web page was not loaded because private or VPN web pages require a High-confidence provider or a provider trusted by your organization's configuration.")));
TB("The web page was not loaded because private or VPN web pages require a High-confidence provider.")));
}
private static bool IsAllowedPrivateHost(string host, IReadOnlyList<AllowedPrivateHostPattern> allowedPrivateHosts)
@@ -14,6 +14,4 @@ public sealed class ToolExecutionContext
public required IReadOnlyDictionary<string, string> SettingsValues { get; init; }
public ConfidenceLevel ProviderConfidence { get; init; } = ConfidenceLevel.UNKNOWN;
public bool ProviderIsTrustedByConfiguration { get; init; }
}
@@ -109,7 +109,6 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService, ILogge
SettingsManager = settingsManager,
SettingsValues = settingsValues,
ProviderConfidence = provider.Provider.GetConfidence(settingsManager).Level,
ProviderIsTrustedByConfiguration = provider.IsTrustedByConfiguration(settingsManager),
}, token);
logger.LogInformation("Completed tool execution. ToolName={ToolName}, ToolCallId={ToolCallId}, DurationMs={DurationMs}, Status={Status}", toolName, toolCallId, stopwatch.ElapsedMilliseconds, ToolInvocationTraceStatus.SUCCESS);