555 lines
26 KiB
C#
555 lines
26 KiB
C#
// -----------------------------------------------------------------------------
|
||
// DslCompletionProvider.cs
|
||
// Sprint: SPRINT_8200_0012_0003_policy_engine_integration
|
||
// Task: PINT-8200-019
|
||
// Description: Provides DSL autocomplete hints for score fields and other constructs
|
||
// -----------------------------------------------------------------------------
|
||
|
||
using System.Collections.Immutable;
|
||
|
||
namespace StellaOps.PolicyDsl;
|
||
|
||
/// <summary>
|
||
/// Provides completion hints for the Stella Policy DSL.
|
||
/// This provider generates structured completion suggestions that can be used
|
||
/// by any editor client (Monaco, VS Code, etc.).
|
||
/// </summary>
|
||
public static class DslCompletionProvider
|
||
{
|
||
/// <summary>
|
||
/// Gets all available completion items grouped by category.
|
||
/// </summary>
|
||
public static DslCompletionCatalog GetCompletionCatalog() => DslCompletionCatalog.Instance;
|
||
|
||
/// <summary>
|
||
/// Gets completion items relevant for the given context.
|
||
/// </summary>
|
||
/// <param name="context">The completion context including cursor position and text.</param>
|
||
/// <returns>Filtered completion items relevant to the context.</returns>
|
||
public static ImmutableArray<DslCompletionItem> GetCompletionsForContext(DslCompletionContext context)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(context);
|
||
|
||
var results = ImmutableArray.CreateBuilder<DslCompletionItem>();
|
||
var catalog = DslCompletionCatalog.Instance;
|
||
|
||
// Check for namespace prefix completion
|
||
if (context.TriggerText.EndsWith("score.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.ScoreFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (context.TriggerText.EndsWith("sbom.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.SbomFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (context.TriggerText.EndsWith("advisory.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.AdvisoryFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (context.TriggerText.EndsWith("vex.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.VexFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (context.TriggerText.EndsWith("signals.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.SignalFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (context.TriggerText.EndsWith("reachability.", StringComparison.Ordinal))
|
||
{
|
||
results.AddRange(catalog.ReachabilityFields);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
// Check for value completion contexts
|
||
if (IsScoreBucketContext(context.TriggerText))
|
||
{
|
||
results.AddRange(catalog.ScoreBuckets);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (IsScoreFlagContext(context.TriggerText))
|
||
{
|
||
results.AddRange(catalog.ScoreFlags);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (IsVexStatusContext(context.TriggerText))
|
||
{
|
||
results.AddRange(catalog.VexStatuses);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
if (IsVexJustificationContext(context.TriggerText))
|
||
{
|
||
results.AddRange(catalog.VexJustifications);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
// Check for action context (after 'then' or 'else')
|
||
if (IsActionContext(context.TriggerText))
|
||
{
|
||
results.AddRange(catalog.Actions);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
// Default: return all top-level completions
|
||
results.AddRange(catalog.Keywords);
|
||
results.AddRange(catalog.Functions);
|
||
results.AddRange(catalog.Namespaces);
|
||
return results.ToImmutable();
|
||
}
|
||
|
||
private static bool IsScoreBucketContext(string text) =>
|
||
text.Contains("score.bucket", StringComparison.OrdinalIgnoreCase) &&
|
||
(text.EndsWith("== ", StringComparison.Ordinal) ||
|
||
text.EndsWith("!= ", StringComparison.Ordinal) ||
|
||
text.EndsWith("in [", StringComparison.Ordinal) ||
|
||
text.EndsWith("== \"", StringComparison.Ordinal));
|
||
|
||
private static bool IsScoreFlagContext(string text) =>
|
||
text.Contains("score.flags", StringComparison.OrdinalIgnoreCase) &&
|
||
(text.EndsWith("contains ", StringComparison.Ordinal) ||
|
||
text.EndsWith("contains \"", StringComparison.Ordinal) ||
|
||
text.EndsWith("in [", StringComparison.Ordinal));
|
||
|
||
private static bool IsVexStatusContext(string text) =>
|
||
text.Contains("status", StringComparison.OrdinalIgnoreCase) &&
|
||
(text.EndsWith("== ", StringComparison.Ordinal) ||
|
||
text.EndsWith(":= ", StringComparison.Ordinal) ||
|
||
text.EndsWith("!= ", StringComparison.Ordinal) ||
|
||
text.EndsWith("== \"", StringComparison.Ordinal) ||
|
||
text.EndsWith(":= \"", StringComparison.Ordinal));
|
||
|
||
private static bool IsVexJustificationContext(string text) =>
|
||
text.Contains("justification", StringComparison.OrdinalIgnoreCase) &&
|
||
(text.EndsWith("== ", StringComparison.Ordinal) ||
|
||
text.EndsWith("!= ", StringComparison.Ordinal) ||
|
||
text.EndsWith("== \"", StringComparison.Ordinal));
|
||
|
||
private static bool IsActionContext(string text)
|
||
{
|
||
var trimmed = text.TrimEnd();
|
||
return trimmed.EndsWith(" then", StringComparison.OrdinalIgnoreCase) ||
|
||
trimmed.EndsWith(" else", StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Context for completion requests.
|
||
/// </summary>
|
||
/// <param name="TriggerText">The text up to and including the cursor position.</param>
|
||
/// <param name="LineNumber">The 1-based line number of the cursor.</param>
|
||
/// <param name="Column">The 1-based column number of the cursor.</param>
|
||
public sealed record DslCompletionContext(
|
||
string TriggerText,
|
||
int LineNumber = 1,
|
||
int Column = 1);
|
||
|
||
/// <summary>
|
||
/// A single completion item.
|
||
/// </summary>
|
||
/// <param name="Label">The display label for the completion.</param>
|
||
/// <param name="Kind">The kind of completion (keyword, field, function, etc.).</param>
|
||
/// <param name="InsertText">The text to insert when the completion is accepted.</param>
|
||
/// <param name="Documentation">Documentation describing the completion item.</param>
|
||
/// <param name="Detail">Additional detail shown in the completion list.</param>
|
||
/// <param name="IsSnippet">Whether the insert text is a snippet with placeholders.</param>
|
||
public sealed record DslCompletionItem(
|
||
string Label,
|
||
DslCompletionKind Kind,
|
||
string InsertText,
|
||
string Documentation,
|
||
string? Detail = null,
|
||
bool IsSnippet = false);
|
||
|
||
/// <summary>
|
||
/// The kind of completion item.
|
||
/// </summary>
|
||
public enum DslCompletionKind
|
||
{
|
||
Keyword = 14,
|
||
Function = 1,
|
||
Field = 5,
|
||
Constant = 21,
|
||
Namespace = 9,
|
||
Snippet = 15,
|
||
}
|
||
|
||
/// <summary>
|
||
/// Catalog of all completion items, organized by category.
|
||
/// </summary>
|
||
public sealed class DslCompletionCatalog
|
||
{
|
||
/// <summary>
|
||
/// Singleton instance of the completion catalog.
|
||
/// </summary>
|
||
public static DslCompletionCatalog Instance { get; } = new();
|
||
|
||
private DslCompletionCatalog()
|
||
{
|
||
// Initialize all completion categories
|
||
Keywords = BuildKeywords();
|
||
Functions = BuildFunctions();
|
||
Namespaces = BuildNamespaces();
|
||
ScoreFields = BuildScoreFields();
|
||
ScoreBuckets = BuildScoreBuckets();
|
||
ScoreFlags = BuildScoreFlags();
|
||
SbomFields = BuildSbomFields();
|
||
AdvisoryFields = BuildAdvisoryFields();
|
||
VexFields = BuildVexFields();
|
||
VexStatuses = BuildVexStatuses();
|
||
VexJustifications = BuildVexJustifications();
|
||
SignalFields = BuildSignalFields();
|
||
ReachabilityFields = BuildReachabilityFields();
|
||
Actions = BuildActions();
|
||
}
|
||
|
||
/// <summary>DSL keywords (policy, rule, when, then, etc.).</summary>
|
||
public ImmutableArray<DslCompletionItem> Keywords { get; }
|
||
|
||
/// <summary>Built-in functions.</summary>
|
||
public ImmutableArray<DslCompletionItem> Functions { get; }
|
||
|
||
/// <summary>Top-level namespaces (score, sbom, advisory, etc.).</summary>
|
||
public ImmutableArray<DslCompletionItem> Namespaces { get; }
|
||
|
||
/// <summary>Score namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> ScoreFields { get; }
|
||
|
||
/// <summary>Score bucket values.</summary>
|
||
public ImmutableArray<DslCompletionItem> ScoreBuckets { get; }
|
||
|
||
/// <summary>Score flag values.</summary>
|
||
public ImmutableArray<DslCompletionItem> ScoreFlags { get; }
|
||
|
||
/// <summary>SBOM namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> SbomFields { get; }
|
||
|
||
/// <summary>Advisory namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> AdvisoryFields { get; }
|
||
|
||
/// <summary>VEX namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> VexFields { get; }
|
||
|
||
/// <summary>VEX status values.</summary>
|
||
public ImmutableArray<DslCompletionItem> VexStatuses { get; }
|
||
|
||
/// <summary>VEX justification values.</summary>
|
||
public ImmutableArray<DslCompletionItem> VexJustifications { get; }
|
||
|
||
/// <summary>Signal namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> SignalFields { get; }
|
||
|
||
/// <summary>Reachability namespace fields.</summary>
|
||
public ImmutableArray<DslCompletionItem> ReachabilityFields { get; }
|
||
|
||
/// <summary>Action keywords and patterns.</summary>
|
||
public ImmutableArray<DslCompletionItem> Actions { get; }
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildKeywords() =>
|
||
[
|
||
new("policy", DslCompletionKind.Keyword, "policy \"${1:PolicyName}\" syntax \"stella-dsl@1\" {\n\t$0\n}",
|
||
"Define a new policy document.", "Policy Declaration", true),
|
||
new("rule", DslCompletionKind.Keyword, "rule ${1:rule_name} priority ${2:10} {\n\twhen ${3:condition}\n\tthen ${4:action}\n\tbecause \"${5:rationale}\";\n}",
|
||
"Define a policy rule with when/then logic.", "Rule Definition", true),
|
||
new("when", DslCompletionKind.Keyword, "when ${1:condition}",
|
||
"Condition clause for rule execution.", "Rule Condition", true),
|
||
new("then", DslCompletionKind.Keyword, "then ${1:action}",
|
||
"Action clause executed when condition is true.", "Rule Action", true),
|
||
new("else", DslCompletionKind.Keyword, "else ${1:action}",
|
||
"Fallback action clause.", "Rule Else Action", true),
|
||
new("because", DslCompletionKind.Keyword, "because \"${1:rationale}\"",
|
||
"Mandatory rationale for status/severity changes.", "Rule Rationale", true),
|
||
new("metadata", DslCompletionKind.Keyword, "metadata {\n\tdescription = \"${1:description}\"\n\ttags = [$2]\n}",
|
||
"Define metadata for the policy.", "Metadata Section", true),
|
||
new("settings", DslCompletionKind.Keyword, "settings {\n\t${1:shadow} = ${2:true};\n}",
|
||
"Configure evaluation settings.", "Settings Section", true),
|
||
new("profile", DslCompletionKind.Keyword, "profile ${1:severity} {\n\t$0\n}",
|
||
"Define a profile block for scoring modifiers.", "Profile Section", true),
|
||
new("and", DslCompletionKind.Keyword, "and", "Logical AND operator."),
|
||
new("or", DslCompletionKind.Keyword, "or", "Logical OR operator."),
|
||
new("not", DslCompletionKind.Keyword, "not", "Logical NOT operator."),
|
||
new("in", DslCompletionKind.Keyword, "in", "Membership test operator."),
|
||
new("between", DslCompletionKind.Keyword, "between ${1:min} and ${2:max}",
|
||
"Range comparison operator.", "Range Check", true),
|
||
new("contains", DslCompletionKind.Keyword, "contains", "Array contains operator."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildFunctions() =>
|
||
[
|
||
new("normalize_cvss", DslCompletionKind.Function, "normalize_cvss(${1:advisory})",
|
||
"Parse advisory for CVSS data and return severity scalar.", "Advisory → SeverityScalar", true),
|
||
new("severity_band", DslCompletionKind.Function, "severity_band(\"${1:severity}\")",
|
||
"Normalise severity string to band.", "string → SeverityBand", true),
|
||
new("risk_score", DslCompletionKind.Function, "risk_score(${1:base}, ${2:modifier})",
|
||
"Calculate risk by multiplying severity × trust × reachability.", "Variadic", true),
|
||
new("exists", DslCompletionKind.Function, "exists(${1:expression})",
|
||
"Return true when value is non-null/empty.", "→ bool", true),
|
||
new("coalesce", DslCompletionKind.Function, "coalesce(${1:a}, ${2:b})",
|
||
"Return first non-null argument.", "→ value", true),
|
||
new("days_between", DslCompletionKind.Function, "days_between(${1:dateA}, ${2:dateB})",
|
||
"Calculate absolute day difference (UTC).", "→ int", true),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildNamespaces() =>
|
||
[
|
||
new("score", DslCompletionKind.Namespace, "score",
|
||
"Evidence-weighted score object. Access via score.value, score.bucket, etc."),
|
||
new("sbom", DslCompletionKind.Namespace, "sbom",
|
||
"SBOM (Software Bill of Materials) data for the finding."),
|
||
new("advisory", DslCompletionKind.Namespace, "advisory",
|
||
"Security advisory information."),
|
||
new("vex", DslCompletionKind.Namespace, "vex",
|
||
"VEX (Vulnerability Exploitability eXchange) statements."),
|
||
new("severity", DslCompletionKind.Namespace, "severity",
|
||
"Severity information for the finding."),
|
||
new("signals", DslCompletionKind.Namespace, "signals",
|
||
"Signal data including trust scores and runtime evidence."),
|
||
new("reachability", DslCompletionKind.Namespace, "reachability",
|
||
"Reachability analysis results."),
|
||
new("entropy", DslCompletionKind.Namespace, "entropy",
|
||
"Entropy and uncertainty metrics."),
|
||
new("env", DslCompletionKind.Namespace, "env",
|
||
"Environment context (dev, staging, prod, etc.)."),
|
||
new("run", DslCompletionKind.Namespace, "run",
|
||
"Runtime context (policy ID, tenant, timestamp)."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildScoreFields() =>
|
||
[
|
||
// Core score value
|
||
new("value", DslCompletionKind.Field, "value",
|
||
"Numeric score value (0-100). Use in comparisons like: score.value >= 80",
|
||
"decimal"),
|
||
|
||
// Bucket access
|
||
new("bucket", DslCompletionKind.Field, "bucket",
|
||
"Score bucket: ActNow, ScheduleNext, Investigate, or Watchlist.",
|
||
"string"),
|
||
new("is_act_now", DslCompletionKind.Field, "is_act_now",
|
||
"True if bucket is ActNow (highest priority).",
|
||
"bool"),
|
||
new("is_schedule_next", DslCompletionKind.Field, "is_schedule_next",
|
||
"True if bucket is ScheduleNext.",
|
||
"bool"),
|
||
new("is_investigate", DslCompletionKind.Field, "is_investigate",
|
||
"True if bucket is Investigate.",
|
||
"bool"),
|
||
new("is_watchlist", DslCompletionKind.Field, "is_watchlist",
|
||
"True if bucket is Watchlist (lowest priority).",
|
||
"bool"),
|
||
|
||
// Individual dimension scores (0-1 normalized)
|
||
new("rch", DslCompletionKind.Field, "rch",
|
||
"Reachability dimension score (0-1 normalized). Alias: reachability",
|
||
"double"),
|
||
new("reachability", DslCompletionKind.Field, "reachability",
|
||
"Reachability dimension score (0-1 normalized). Alias: rch",
|
||
"double"),
|
||
new("rts", DslCompletionKind.Field, "rts",
|
||
"Runtime signal dimension score (0-1 normalized). Alias: runtime",
|
||
"double"),
|
||
new("runtime", DslCompletionKind.Field, "runtime",
|
||
"Runtime signal dimension score (0-1 normalized). Alias: rts",
|
||
"double"),
|
||
new("bkp", DslCompletionKind.Field, "bkp",
|
||
"Backport dimension score (0-1 normalized). Alias: backport",
|
||
"double"),
|
||
new("backport", DslCompletionKind.Field, "backport",
|
||
"Backport dimension score (0-1 normalized). Alias: bkp",
|
||
"double"),
|
||
new("xpl", DslCompletionKind.Field, "xpl",
|
||
"Exploit evidence dimension score (0-1 normalized). Alias: exploit",
|
||
"double"),
|
||
new("exploit", DslCompletionKind.Field, "exploit",
|
||
"Exploit evidence dimension score (0-1 normalized). Alias: xpl",
|
||
"double"),
|
||
new("src", DslCompletionKind.Field, "src",
|
||
"Source trust dimension score (0-1 normalized). Alias: source_trust",
|
||
"double"),
|
||
new("source_trust", DslCompletionKind.Field, "source_trust",
|
||
"Source trust dimension score (0-1 normalized). Alias: src",
|
||
"double"),
|
||
new("mit", DslCompletionKind.Field, "mit",
|
||
"Mitigation dimension score (0-1 normalized). Alias: mitigation",
|
||
"double"),
|
||
new("mitigation", DslCompletionKind.Field, "mitigation",
|
||
"Mitigation dimension score (0-1 normalized). Alias: mit",
|
||
"double"),
|
||
|
||
// Flags
|
||
new("flags", DslCompletionKind.Field, "flags",
|
||
"Array of score flags (e.g., \"kev\", \"live-signal\", \"vendor-na\").",
|
||
"string[]"),
|
||
|
||
// Metadata
|
||
new("policy_digest", DslCompletionKind.Field, "policy_digest",
|
||
"SHA-256 digest of the policy used for scoring.",
|
||
"string"),
|
||
new("calculated_at", DslCompletionKind.Field, "calculated_at",
|
||
"ISO 8601 timestamp when score was calculated.",
|
||
"DateTime"),
|
||
new("explanations", DslCompletionKind.Field, "explanations",
|
||
"Array of human-readable explanations for the score.",
|
||
"string[]"),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildScoreBuckets() =>
|
||
[
|
||
new("ActNow", DslCompletionKind.Constant, "\"ActNow\"",
|
||
"Highest priority: immediate action required."),
|
||
new("ScheduleNext", DslCompletionKind.Constant, "\"ScheduleNext\"",
|
||
"High priority: schedule remediation soon."),
|
||
new("Investigate", DslCompletionKind.Constant, "\"Investigate\"",
|
||
"Medium priority: requires investigation."),
|
||
new("Watchlist", DslCompletionKind.Constant, "\"Watchlist\"",
|
||
"Low priority: monitor for changes."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildScoreFlags() =>
|
||
[
|
||
new("kev", DslCompletionKind.Constant, "\"kev\"",
|
||
"Known Exploited Vulnerability (CISA KEV list)."),
|
||
new("live-signal", DslCompletionKind.Constant, "\"live-signal\"",
|
||
"Runtime evidence detected active exploitation."),
|
||
new("vendor-na", DslCompletionKind.Constant, "\"vendor-na\"",
|
||
"Vendor confirms not affected."),
|
||
new("epss-high", DslCompletionKind.Constant, "\"epss-high\"",
|
||
"High EPSS probability score."),
|
||
new("reachable", DslCompletionKind.Constant, "\"reachable\"",
|
||
"Code is statically or dynamically reachable."),
|
||
new("unreachable", DslCompletionKind.Constant, "\"unreachable\"",
|
||
"Code is confirmed unreachable."),
|
||
new("backported", DslCompletionKind.Constant, "\"backported\"",
|
||
"Fix has been backported by vendor."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildSbomFields() =>
|
||
[
|
||
new("purl", DslCompletionKind.Field, "purl", "Package URL of the component."),
|
||
new("name", DslCompletionKind.Field, "name", "Component name."),
|
||
new("version", DslCompletionKind.Field, "version", "Component version."),
|
||
new("licenses", DslCompletionKind.Field, "licenses", "Component licenses."),
|
||
new("layerDigest", DslCompletionKind.Field, "layerDigest", "Container layer digest."),
|
||
new("tags", DslCompletionKind.Field, "tags", "Component tags."),
|
||
new("usedByEntrypoint", DslCompletionKind.Field, "usedByEntrypoint",
|
||
"Whether component is used by entrypoint."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildAdvisoryFields() =>
|
||
[
|
||
new("id", DslCompletionKind.Field, "id", "Advisory identifier."),
|
||
new("source", DslCompletionKind.Field, "source", "Advisory source (GHSA, OSV, etc.)."),
|
||
new("aliases", DslCompletionKind.Field, "aliases", "Advisory aliases (CVE, etc.)."),
|
||
new("severity", DslCompletionKind.Field, "severity", "Advisory severity."),
|
||
new("cvss", DslCompletionKind.Field, "cvss", "CVSS score."),
|
||
new("publishedAt", DslCompletionKind.Field, "publishedAt", "Publication date."),
|
||
new("modifiedAt", DslCompletionKind.Field, "modifiedAt", "Last modification date."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildVexFields() =>
|
||
[
|
||
new("status", DslCompletionKind.Field, "status", "VEX status."),
|
||
new("justification", DslCompletionKind.Field, "justification", "VEX justification."),
|
||
new("statementId", DslCompletionKind.Field, "statementId", "VEX statement ID."),
|
||
new("timestamp", DslCompletionKind.Field, "timestamp", "VEX timestamp."),
|
||
new("scope", DslCompletionKind.Field, "scope", "VEX scope."),
|
||
new("any", DslCompletionKind.Function, "any(${1:predicate})",
|
||
"True if any VEX statement satisfies the predicate.", "(Statement → bool) → bool", true),
|
||
new("all", DslCompletionKind.Function, "all(${1:predicate})",
|
||
"True if all VEX statements satisfy the predicate.", "(Statement → bool) → bool", true),
|
||
new("latest", DslCompletionKind.Function, "latest()",
|
||
"Return the lexicographically newest VEX statement.", "→ Statement", true),
|
||
new("count", DslCompletionKind.Function, "count(${1:predicate})",
|
||
"Count VEX statements matching predicate.", "→ int", true),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildVexStatuses() =>
|
||
[
|
||
new("affected", DslCompletionKind.Constant, "\"affected\"",
|
||
"Component is affected by the vulnerability."),
|
||
new("not_affected", DslCompletionKind.Constant, "\"not_affected\"",
|
||
"Component is not affected."),
|
||
new("fixed", DslCompletionKind.Constant, "\"fixed\"",
|
||
"Vulnerability has been fixed."),
|
||
new("suppressed", DslCompletionKind.Constant, "\"suppressed\"",
|
||
"Finding is suppressed."),
|
||
new("under_investigation", DslCompletionKind.Constant, "\"under_investigation\"",
|
||
"Under investigation."),
|
||
new("escalated", DslCompletionKind.Constant, "\"escalated\"",
|
||
"Finding has been escalated."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildVexJustifications() =>
|
||
[
|
||
new("component_not_present", DslCompletionKind.Constant, "\"component_not_present\"",
|
||
"Component is not present in the product."),
|
||
new("vulnerable_code_not_present", DslCompletionKind.Constant, "\"vulnerable_code_not_present\"",
|
||
"Vulnerable code is not present."),
|
||
new("vulnerable_code_not_in_execute_path", DslCompletionKind.Constant, "\"vulnerable_code_not_in_execute_path\"",
|
||
"Vulnerable code is not in execution path."),
|
||
new("vulnerable_code_cannot_be_controlled_by_adversary", DslCompletionKind.Constant, "\"vulnerable_code_cannot_be_controlled_by_adversary\"",
|
||
"Vulnerable code cannot be controlled by adversary."),
|
||
new("inline_mitigations_already_exist", DslCompletionKind.Constant, "\"inline_mitigations_already_exist\"",
|
||
"Inline mitigations already exist."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildSignalFields() =>
|
||
[
|
||
new("trust_score", DslCompletionKind.Field, "trust_score",
|
||
"Trust score (0–1)."),
|
||
new("reachability.state", DslCompletionKind.Field, "reachability.state",
|
||
"Reachability state."),
|
||
new("reachability.score", DslCompletionKind.Field, "reachability.score",
|
||
"Reachability score (0–1)."),
|
||
new("entropy_penalty", DslCompletionKind.Field, "entropy_penalty",
|
||
"Entropy penalty (0–0.3)."),
|
||
new("uncertainty.level", DslCompletionKind.Field, "uncertainty.level",
|
||
"Uncertainty level (U1–U3)."),
|
||
new("runtime_hits", DslCompletionKind.Field, "runtime_hits",
|
||
"Runtime hit indicator."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildReachabilityFields() =>
|
||
[
|
||
new("state", DslCompletionKind.Field, "state",
|
||
"Reachability state (reachable, unreachable, unknown)."),
|
||
new("score", DslCompletionKind.Field, "score",
|
||
"Reachability confidence score (0–1)."),
|
||
new("callchain", DslCompletionKind.Field, "callchain",
|
||
"Call chain evidence if reachable."),
|
||
new("tool", DslCompletionKind.Field, "tool",
|
||
"Tool that determined reachability."),
|
||
];
|
||
|
||
private static ImmutableArray<DslCompletionItem> BuildActions() =>
|
||
[
|
||
new("status :=", DslCompletionKind.Keyword, "status := \"${1:status}\"",
|
||
"Set the finding status.", "Status Assignment", true),
|
||
new("severity :=", DslCompletionKind.Keyword, "severity := ${1:expression}",
|
||
"Set the finding severity.", "Severity Assignment", true),
|
||
new("ignore", DslCompletionKind.Keyword, "ignore until ${1:date} because \"${2:rationale}\"",
|
||
"Temporarily suppress finding until date.", "Ignore Action", true),
|
||
new("escalate", DslCompletionKind.Keyword, "escalate to severity_band(\"${1:severity}\") when ${2:condition}",
|
||
"Escalate severity when condition is true.", "Escalate Action", true),
|
||
new("warn", DslCompletionKind.Keyword, "warn message \"${1:text}\"",
|
||
"Add warning verdict.", "Warn Action", true),
|
||
new("defer", DslCompletionKind.Keyword, "defer until ${1:condition}",
|
||
"Defer finding evaluation.", "Defer Action", true),
|
||
new("annotate", DslCompletionKind.Keyword, "annotate ${1:key} := ${2:value}",
|
||
"Add free-form annotation to explain payload.", "Annotate Action", true),
|
||
new("requireVex", DslCompletionKind.Keyword, "requireVex {\n\tvendors = [${1:\"Vendor\"}]\n\tjustifications = [${2:\"component_not_present\"}]\n}",
|
||
"Require matching VEX evidence.", "Require VEX Action", true),
|
||
];
|
||
}
|