sln build fix (again), tests fixes, audit work and doctors work
This commit is contained in:
@@ -124,25 +124,25 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
private readonly ILogger<AdvisoryChatIntentRouter> _logger;
|
||||
|
||||
// Regex patterns for slash commands - compiled for performance
|
||||
[GeneratedRegex(@"^/explain\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+)\s+in\s+(?<image>\S+)\s+(?<env>\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/explain\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+)(?:\s+in\s+(?<image>\S+)(?:\s+(?<env>\S+))?)?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ExplainPattern();
|
||||
|
||||
[GeneratedRegex(@"^/is[_-]?it[_-]?reachable\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+|[^@\s]+)\s+in\s+(?<image>\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/is[_-]?it[_-]?reachable\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+|[^@\s]+)(?:\s+in\s+(?<image>\S+))?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ReachablePattern();
|
||||
|
||||
[GeneratedRegex(@"^/do[_-]?we[_-]?have[_-]?a[_-]?backport\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+)\s+in\s+(?<package>\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/do[_-]?we[_-]?have[_-]?a[_-]?backport\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+)(?:\s+in\s+(?<package>\S+))?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex BackportPattern();
|
||||
|
||||
[GeneratedRegex(@"^/propose[_-]?fix\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+|\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ProposeFixPattern();
|
||||
|
||||
[GeneratedRegex(@"^/waive\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+|\S+)\s+for\s+(?<duration>\d+[dhwm])\s+because\s+(?<reason>.+)$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/waive\s+(?<finding>CVE-\d{4}-\d+|GHSA-[a-z0-9-]+|\S+)(?:\s+(?:for\s+)?(?<duration>\d+[dhwm]))?(?:\s+(?:because\s+)?(?<reason>.+))?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex WaivePattern();
|
||||
|
||||
[GeneratedRegex(@"^/batch[_-]?triage\s+(?:top\s+)?(?<top>\d+)\s+(?:findings\s+)?in\s+(?<env>\S+)(?:\s+by\s+(?<method>\S+))?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/batch[_-]?triage(?:\s+(?:top\s+)?(?<top>\d+))?(?:\s+(?:findings\s+)?in\s+(?<env>\S+))?(?:\s+by\s+(?<method>\S+))?|^/batch[_-]?triage\s+(?<priority>\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex BatchTriagePattern();
|
||||
|
||||
[GeneratedRegex(@"^/compare\s+(?<env1>\S+)\s+vs\s+(?<env2>\S+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
[GeneratedRegex(@"^/compare\s+(?<env1>\S+)\s+(?:vs\s+)?(?<env2>\S+)?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ComparePattern();
|
||||
|
||||
// Patterns for CVE/GHSA extraction
|
||||
@@ -281,8 +281,8 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
Parameters = new IntentParameters
|
||||
{
|
||||
FindingId = waiveMatch.Groups["finding"].Value.ToUpperInvariant(),
|
||||
Duration = waiveMatch.Groups["duration"].Value,
|
||||
Reason = waiveMatch.Groups["reason"].Value
|
||||
Duration = waiveMatch.Groups["duration"].Success ? waiveMatch.Groups["duration"].Value : null,
|
||||
Reason = waiveMatch.Groups["reason"].Success ? waiveMatch.Groups["reason"].Value : null
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -292,6 +292,7 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
if (batchMatch.Success)
|
||||
{
|
||||
_ = int.TryParse(batchMatch.Groups["top"].Value, out var topN);
|
||||
var priority = batchMatch.Groups["priority"].Success ? batchMatch.Groups["priority"].Value : null;
|
||||
return new IntentRoutingResult
|
||||
{
|
||||
Intent = AdvisoryChatIntent.BatchTriage,
|
||||
@@ -301,10 +302,10 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
Parameters = new IntentParameters
|
||||
{
|
||||
TopN = topN > 0 ? topN : 10,
|
||||
Environment = batchMatch.Groups["env"].Value,
|
||||
Environment = batchMatch.Groups["env"].Success ? batchMatch.Groups["env"].Value : null,
|
||||
PriorityMethod = batchMatch.Groups["method"].Success
|
||||
? batchMatch.Groups["method"].Value
|
||||
: "exploit_pressure"
|
||||
: priority ?? "exploit_pressure"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -335,22 +336,23 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
var lowerInput = input.ToLowerInvariant();
|
||||
var parameters = ExtractParametersFromContent(input);
|
||||
|
||||
// Keywords for each intent
|
||||
// Keywords for each intent - ordered by specificity
|
||||
// Use phrases to avoid false positives from single words
|
||||
var explainKeywords = new[] { "explain", "what does", "what is", "tell me about", "describe", "mean" };
|
||||
var reachableKeywords = new[] { "reachable", "reach", "call", "path", "accessible", "executed" };
|
||||
var backportKeywords = new[] { "backport", "patch", "binary", "distro fix", "security update" };
|
||||
var fixKeywords = new[] { "fix", "remediate", "resolve", "mitigate", "patch", "upgrade", "update" };
|
||||
var backportKeywords = new[] { "backport", "binary", "distro fix", "security update" };
|
||||
var fixKeywords = new[] { "fix", "remediat", "remediation", "resolve", "mitigate", "upgrade", "update", "how do i", "how can i", "options for", "patch option", "patch for" };
|
||||
var waiveKeywords = new[] { "waive", "accept risk", "exception", "defer", "skip" };
|
||||
var triageKeywords = new[] { "triage", "prioritize", "batch", "top", "most important", "critical" };
|
||||
var compareKeywords = new[] { "compare", "difference", "vs", "versus", "between" };
|
||||
|
||||
// Score each intent
|
||||
// Score each intent with weighted keywords
|
||||
var scores = new Dictionary<AdvisoryChatIntent, double>
|
||||
{
|
||||
[AdvisoryChatIntent.Explain] = ScoreKeywords(lowerInput, explainKeywords),
|
||||
[AdvisoryChatIntent.IsItReachable] = ScoreKeywords(lowerInput, reachableKeywords),
|
||||
[AdvisoryChatIntent.DoWeHaveABackport] = ScoreKeywords(lowerInput, backportKeywords),
|
||||
[AdvisoryChatIntent.ProposeFix] = ScoreKeywords(lowerInput, fixKeywords),
|
||||
[AdvisoryChatIntent.ProposeFix] = ScoreKeywordsWeighted(lowerInput, fixKeywords),
|
||||
[AdvisoryChatIntent.Waive] = ScoreKeywords(lowerInput, waiveKeywords),
|
||||
[AdvisoryChatIntent.BatchTriage] = ScoreKeywords(lowerInput, triageKeywords),
|
||||
[AdvisoryChatIntent.Compare] = ScoreKeywords(lowerInput, compareKeywords)
|
||||
@@ -362,7 +364,7 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
.First();
|
||||
|
||||
// If no strong signal, default to Explain if we have a CVE, otherwise General
|
||||
if (bestScore < 0.3)
|
||||
if (bestScore < 0.15)
|
||||
{
|
||||
if (parameters.FindingId is not null)
|
||||
{
|
||||
@@ -437,6 +439,21 @@ internal sealed partial class AdvisoryChatIntentRouter : IAdvisoryChatIntentRout
|
||||
return matches / (double)keywords.Length;
|
||||
}
|
||||
|
||||
private static double ScoreKeywordsWeighted(string input, string[] keywords)
|
||||
{
|
||||
// Give higher weight to phrase matches
|
||||
double score = 0;
|
||||
foreach (var keyword in keywords)
|
||||
{
|
||||
if (input.Contains(keyword, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Multi-word phrases get higher weight
|
||||
score += keyword.Contains(' ') ? 0.4 : 0.2;
|
||||
}
|
||||
}
|
||||
return Math.Min(score, 1.0);
|
||||
}
|
||||
|
||||
private static string TruncateForLog(string input)
|
||||
{
|
||||
const int maxLength = 100;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Advisory AI Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
|
||||
Reference in New Issue
Block a user