Skip to content

Commit eacbe33

Browse files
committed
AI Service configuration, UI improvements, and NuGet vulnerability fixes
1 parent 03ff158 commit eacbe33

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+4783
-28
lines changed

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,11 @@ __pycache__/
264264
*.rsuser
265265
*.rsuser
266266
DBADashBuild/
267-
Visual Studio 2022/
267+
Visual Studio 2022/
268+
# Local AI feedback samples (may contain sensitive environment data)
269+
DBADashAI/ChatFeedback.txt
270+
271+
# Local process/tooling files not required for product runtime
272+
.github/copilot-instructions.md
273+
Docs/Secure-Contribution-Checklist.md
274+
Scripts/security/

DBADash.Test/DBADash.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</PropertyGroup>
1313

1414
<ItemGroup>
15+
<PackageReference Include="MailKit" Version="4.16.0" />
1516
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
1617
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.5" />
1718
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
@@ -22,6 +23,7 @@
2223
<PrivateAssets>all</PrivateAssets>
2324
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2425
</PackageReference>
26+
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.6" />
2527
</ItemGroup>
2628

2729
<ItemGroup>

DBADash.sln

Lines changed: 192 additions & 0 deletions
Large diffs are not rendered by default.

DBADash/CollectionConfig.cs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,38 @@ public List<DBADashSource> GetNewAzureDBConnections(DBADashSource masterConnecti
468468
return newConnections;
469469
}
470470

471+
private string EncryptAiSecretIfNeeded(string value)
472+
{
473+
if (value == "")
474+
{
475+
return null;
476+
}
477+
478+
if (value == null || value.StartsWith("¬=!"))
479+
{
480+
return value;
481+
}
482+
483+
wasEncryptionPerformed = true;
484+
return "¬=!" + value.EncryptString(myString);
485+
}
486+
487+
private string DecryptAiSecretIfNeeded(string value)
488+
{
489+
if (!string.IsNullOrEmpty(value) && value.StartsWith("¬=!"))
490+
{
491+
return value[3..].DecryptString(myString);
492+
}
493+
494+
return value;
495+
}
496+
471497
public override bool ContainsSensitive()
472498
{
473-
if (!string.IsNullOrEmpty(SecretKey))
499+
if (!string.IsNullOrEmpty(SecretKey)
500+
|| !string.IsNullOrEmpty(AzureOpenAIApiKey)
501+
|| !string.IsNullOrEmpty(AnthropicApiKey)
502+
|| !string.IsNullOrEmpty(OpenCodeApiKey))
474503
{
475504
return true;
476505
}
@@ -553,5 +582,55 @@ public async Task<DBADashSource> FindSourceConnectionAsync(string connectionStri
553582

554583
return source;
555584
}
585+
586+
public string AIProvider { get; set; }
587+
588+
public string AzureOpenAIEndpoint { get; set; }
589+
590+
private string _azureOpenAIApiKey;
591+
public string AzureOpenAIApiKey
592+
{
593+
get => _azureOpenAIApiKey;
594+
set => _azureOpenAIApiKey = EncryptAiSecretIfNeeded(value);
595+
}
596+
597+
[JsonIgnore]
598+
public string AzureOpenAIApiKeyDecrypted => DecryptAiSecretIfNeeded(_azureOpenAIApiKey);
599+
600+
public string AzureOpenAIDeployment { get; set; }
601+
602+
public string AzureOpenAIApiVersion { get; set; }
603+
604+
public string AnthropicBaseUrl { get; set; }
605+
606+
private string _anthropicApiKey;
607+
public string AnthropicApiKey
608+
{
609+
get => _anthropicApiKey;
610+
set => _anthropicApiKey = EncryptAiSecretIfNeeded(value);
611+
}
612+
613+
[JsonIgnore]
614+
public string AnthropicApiKeyDecrypted => DecryptAiSecretIfNeeded(_anthropicApiKey);
615+
616+
public string AnthropicModel { get; set; }
617+
618+
public string AnthropicVersion { get; set; }
619+
620+
public string AnthropicMaxTokens { get; set; }
621+
622+
public string OpenCodeBaseUrl { get; set; }
623+
624+
private string _openCodeApiKey;
625+
public string OpenCodeApiKey
626+
{
627+
get => _openCodeApiKey;
628+
set => _openCodeApiKey = EncryptAiSecretIfNeeded(value);
629+
}
630+
631+
[JsonIgnore]
632+
public string OpenCodeApiKeyDecrypted => DecryptAiSecretIfNeeded(_openCodeApiKey);
633+
634+
public string OpenCodeModel { get; set; }
556635
}
557636
}

DBADash/DBADash.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
<PackageReference Include="Azure.Core" Version="1.52.0" />
121121
<PackageReference Include="Azure.Identity" Version="1.20.0" />
122122
<PackageReference Include="Humanizer.Core" Version="3.0.10" />
123-
<PackageReference Include="MailKit" Version="4.15.1" />
123+
<PackageReference Include="MailKit" Version="4.16.0" />
124124
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5" />
125125
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
126126
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />

DBADash/ServiceTools.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ public enum StartMode
102102
}
103103

104104
public static string GetServiceInstallArgs(string serviceName, string userName, string password, StartMode mode = StartMode.AutomaticDelayedStart)
105+
{
106+
return GetServiceInstallArgs(serviceName, ServicePath, userName, password, mode);
107+
}
108+
109+
public static string GetServiceInstallArgs(string serviceName, string binPath, string userName, string password, StartMode mode = StartMode.AutomaticDelayedStart)
105110
{
106111
var modeString = mode switch
107112
{
@@ -111,7 +116,7 @@ public static string GetServiceInstallArgs(string serviceName, string userName,
111116
StartMode.Disabled => "disabled",
112117
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
113118
};
114-
var args = $"create {serviceName} binpath=\"{ServicePath}\" start={modeString}";
119+
var args = $"create {serviceName} binpath=\"{binPath}\" start={modeString}";
115120
if (!string.IsNullOrEmpty(userName))
116121
{
117122
args += $" obj=\"{userName}\"";
@@ -309,6 +314,12 @@ private static SCCommandResult SetServiceRecovery(string serviceName)
309314

310315
public static SCCommandResult InstallService(string serviceName, string userName, string password,
311316
StartMode mode = StartMode.AutomaticDelayedStart)
317+
{
318+
return InstallService(serviceName, ServicePath, "Monitoring tool for SQL Server. https://dbadash.com", userName, password, mode);
319+
}
320+
321+
public static SCCommandResult InstallService(string serviceName, string binPath, string description, string userName, string password,
322+
StartMode mode = StartMode.AutomaticDelayedStart)
312323
{
313324
var result = new SCCommandResult();
314325
var outputBuilder = new StringBuilder();
@@ -322,8 +333,8 @@ public static SCCommandResult InstallService(string serviceName, string userName
322333
}
323334

324335
using Process p = new();
325-
var args = GetServiceInstallArgs(serviceName, userName, password, mode);
326-
var DebugArgs = GetServiceInstallArgs(serviceName, userName, string.IsNullOrEmpty(password) ? string.Empty : "*****", mode);
336+
var args = GetServiceInstallArgs(serviceName, binPath, userName, password, mode);
337+
var DebugArgs = GetServiceInstallArgs(serviceName, binPath, userName, string.IsNullOrEmpty(password) ? string.Empty : "*****", mode);
327338
Log.Information($"sc.exe {DebugArgs}");
328339
var psi = new ProcessStartInfo
329340
{
@@ -350,7 +361,7 @@ public static SCCommandResult InstallService(string serviceName, string userName
350361
if (p.ExitCode == 0)
351362
{
352363
Log.Information("Setting service description");
353-
var descriptionResult = SetServiceDescription(serviceName, "Monitoring tool for SQL Server. https://dbadash.com");
364+
var descriptionResult = SetServiceDescription(serviceName, description);
354365
if (!descriptionResult.Success)
355366
{
356367
Log.Error("Error setting service description: {result}", descriptionResult.Output);

DBADashAI/DBADashAI.csproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>net10.0</TargetFramework>
4+
<Nullable>enable</Nullable>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.4.0" />
10+
<PackageReference Include="Azure.Identity" Version="1.20.0" />
11+
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
12+
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.6" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\DBADash\DBADash.csproj" />
17+
</ItemGroup>
18+
</Project>

DBADashAI/Models/AiAskRequest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace DBADashAI.Models;
2+
3+
public sealed class AiAskRequest
4+
{
5+
public const int MaxQuestionLength = 2000;
6+
public const int MaxAllowedRows = 200;
7+
8+
public string Question { get; set; } = string.Empty;
9+
10+
public string? ToolName { get; set; }
11+
12+
public bool IncludeAiSummary { get; set; } = true;
13+
14+
public int MaxRows { get; set; } = 50;
15+
16+
public string? Validate()
17+
{
18+
if (string.IsNullOrWhiteSpace(Question))
19+
{
20+
return "Question is required.";
21+
}
22+
23+
if (Question.Length > MaxQuestionLength)
24+
{
25+
return $"Question exceeds maximum length of {MaxQuestionLength} characters.";
26+
}
27+
28+
if (MaxRows < 1 || MaxRows > MaxAllowedRows)
29+
{
30+
return $"MaxRows must be between 1 and {MaxAllowedRows}.";
31+
}
32+
33+
return null;
34+
}
35+
}

DBADashAI/Models/AiAskResponse.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace DBADashAI.Models;
2+
3+
public sealed class AiAskResponse
4+
{
5+
public string RequestId { get; set; } = string.Empty;
6+
7+
public string Tool { get; set; } = string.Empty;
8+
9+
public object Data { get; set; } = new();
10+
11+
public string? Summary { get; set; }
12+
13+
public List<AiEvidenceItem> Evidence { get; set; } = [];
14+
15+
public long ToolExecutionMs { get; set; }
16+
17+
public long TotalExecutionMs { get; set; }
18+
19+
public double ConfidenceScore { get; set; }
20+
21+
public string ConfidenceLabel { get; set; } = "Low";
22+
}

DBADashAI/Models/AiEvidenceItem.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DBADashAI.Models;
2+
3+
public sealed class AiEvidenceItem
4+
{
5+
public string Source { get; set; } = string.Empty;
6+
7+
public string Detail { get; set; } = string.Empty;
8+
9+
public int Rank { get; set; }
10+
11+
public double Score { get; set; }
12+
}

0 commit comments

Comments
 (0)