Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
111824c
Initial plan
Copilot Feb 4, 2026
a19cd6f
Fix VS2022 extension by adding specific content type registrations
Copilot Feb 4, 2026
b206264
Rewrite VS Extension for new VS SDK +VS 2026 compatibility.
gfs Feb 5, 2026
5ea212b
Fix code fixes + suppressions.
gfs Feb 5, 2026
4572df2
Clean up language server process
gfs Feb 5, 2026
21d8566
Fix settings
gfs Feb 6, 2026
b5cb808
Delete DevSkim-DotNet/devskim-server-log20260205.txt
gfs Feb 6, 2026
461db4d
Simplify diagnostic key - alraedy mapped in dictionary by uri no need…
gfs Feb 6, 2026
e241b3e
Merge branch 'copilot/investigate-devskim-issue' of https://github.co…
gfs Feb 6, 2026
61c2fc3
Fix version stamping extension
gfs Feb 6, 2026
c9824e2
Remove custom fix handler (now redundant)
gfs Feb 6, 2026
8b9871d
Fix metadata in VS Extension
gfs Feb 6, 2026
07325b3
Update DevSkim-DotNet/Microsoft.DevSkim.LanguageServer/Program.cs
gfs Feb 6, 2026
f7e548e
Remove inadvertent detection test change.
gfs Feb 6, 2026
3d185d9
Address code review feedback: use Select() and remove unused code
Copilot Feb 6, 2026
f36a241
Merge branch 'copilot/investigate-devskim-issue' of https://github.co…
gfs Feb 6, 2026
ed6cf52
Add documentation for P/Invoke usage in Job Object code
Copilot Feb 6, 2026
331529c
Code review improvements/cleanup.
gfs Feb 6, 2026
8188425
Merge branch 'copilot/investigate-devskim-issue' of https://github.co…
gfs Feb 6, 2026
2d8c0a7
Update ESLint ecosystem to v9, resolving 6 of 8 npm deprecation war…
gfs Feb 6, 2026
2fc2b72
Add CancellationTokenSource for graceful subscription cancellation
Copilot Feb 6, 2026
89d0661
Merge branch 'main' into copilot/investigate-devskim-issue
gfs Feb 6, 2026
31b13f8
Add changelog entry for VS extension rewrite using VisualStudio.Exten…
Copilot Feb 6, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ DevSkim-DotNet/Microsoft.DevSkim.sln.DotSettings.user
DevSkim-VSCode-Plugin/client/dist/*
DevSkim-VSCode-Plugin/devskimBinaries/*
DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/generatedLanguageServerBinaries/*
DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/Server/*

# Debug artifacts
DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/devskim-server-*.txt
Expand Down
8 changes: 8 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.76] - 2026-02-06
### Changed
- Rewrote Visual Studio extension using VisualStudio.Extensibility SDK for VS2022/2026 compatibility
- Replaced legacy MEF-based ILanguageClient with LanguageServerProvider from new VS Extensibility SDK
- Implemented Windows Job Object for reliable language server process cleanup when VS exits
- Added settings management using VS Extensibility SDK settings API with localized string resources
- Enhanced code actions and fixes handling for better VS compatibility

## [1.0.75] - 2026-02-06
### Changed
- Removed unnecessary uninstall/reinstall of @vscode/vsce from postinstall script in VSCode plugin
Expand Down

This file was deleted.

77 changes: 0 additions & 77 deletions DevSkim-DotNet/ManualGenerator/Program.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ public class PortableScannerSettings : IDevSkimOptions
public bool RemoveFindingsOnClose { get; set; } = true;
public string CustomLanguagesPath { get; set; } = string.Empty;
public string CustomCommentsPath { get; set; } = string.Empty;
public string GuidanceBaseURL { get; set; }
public bool EnableCriticalSeverityRules { get; set; }
public bool EnableImportantSeverityRules { get; set; }
public bool EnableModerateSeverityRules { get; set; }
public bool EnableManualReviewSeverityRules { get; set; }
public bool EnableBestPracticeSeverityRules { get; set; }
public bool EnableHighConfidenceRules { get; set; }
public bool EnableLowConfidenceRules { get; set; }
public bool EnableMediumConfidenceRules { get; set; }
public string GuidanceBaseURL { get; set; } = "https://github.com/microsoft/devskim/tree/main/guidance";
// Default all severity rules to enabled
public bool EnableCriticalSeverityRules { get; set; } = true;
public bool EnableImportantSeverityRules { get; set; } = true;
public bool EnableModerateSeverityRules { get; set; } = true;
public bool EnableManualReviewSeverityRules { get; set; } = true;
public bool EnableBestPracticeSeverityRules { get; set; } = true;
// Default high and medium confidence to enabled, low disabled
public bool EnableHighConfidenceRules { get; set; } = true;
public bool EnableLowConfidenceRules { get; set; } = false;
public bool EnableMediumConfidenceRules { get; set; } = true;
}
}
168 changes: 168 additions & 0 deletions DevSkim-DotNet/Microsoft.DevSkim.LanguageServer/CodeActionHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Concurrent;
using MediatR;
using Microsoft.DevSkim;
using Microsoft.DevSkim.LanguageProtoInterop;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace DevSkim.LanguageServer
{
/// <summary>
/// Handles textDocument/codeAction requests from the LSP client.
/// Provides quick fixes for DevSkim security findings.
/// </summary>
internal class CodeActionHandler : ICodeActionHandler
{
private readonly ILogger<CodeActionHandler> _logger;

// Store code fixes keyed by document URI and diagnostic key
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, List<CodeFixMapping>>> _codeFixCache = new();

// Store line lengths per document so we can compute exact end-of-line positions for suppressions
private static readonly ConcurrentDictionary<string, int[]> _lineLengthCache = new();

public CodeActionHandler(ILogger<CodeActionHandler> logger)
{
_logger = logger;
}

public CodeActionRegistrationOptions GetRegistrationOptions(CodeActionCapability capability, ClientCapabilities clientCapabilities)
{
return new CodeActionRegistrationOptions
{
DocumentSelector = TextDocumentSelector.ForPattern("**/*"),
CodeActionKinds = new Container<CodeActionKind>(CodeActionKind.QuickFix),
ResolveProvider = false
};
}

public Task<CommandOrCodeActionContainer?> Handle(CodeActionParams request, CancellationToken cancellationToken)
{
var result = new List<CommandOrCodeAction>();
var documentUri = request.TextDocument.Uri.ToString();

_logger.LogDebug($"CodeActionHandler: Processing request for {documentUri}");
_logger.LogDebug($"CodeActionHandler: {request.Context.Diagnostics.Count()} diagnostics in context");

foreach (var diagnostic in request.Context.Diagnostics)
{
// Only process DevSkim diagnostics
if (diagnostic.Source != "DevSkim Language Server")
{
continue;
}

var diagnosticKey = CreateDiagnosticKey(documentUri, diagnostic);

if (_codeFixCache.TryGetValue(documentUri, out var documentFixes) &&
documentFixes.TryGetValue(diagnosticKey, out var fixes))
{
_logger.LogDebug($"CodeActionHandler: Found {fixes.Count} fixes");
var codeActions = fixes.Select(fix => new CodeAction
{
Title = fix.friendlyString,
Kind = CodeActionKind.QuickFix,
Diagnostics = new Container<Diagnostic>(diagnostic),
Edit = CreateWorkspaceEdit(request.TextDocument.Uri, diagnostic, fix)
});
result.AddRange(codeActions.Select(ca => new CommandOrCodeAction(ca)));
}
}

_logger.LogDebug($"CodeActionHandler: Returning {result.Count} code actions");
return Task.FromResult<CommandOrCodeActionContainer?>(new CommandOrCodeActionContainer(result));
}

private static WorkspaceEdit CreateWorkspaceEdit(DocumentUri uri, Diagnostic diagnostic, CodeFixMapping fix)
{
TextEdit textEdit;
if (fix.isSuppression)
{
// For suppressions, insert at the actual end of the line
int line = diagnostic.Range.End.Line;
int lineLength = GetLineLength(uri, line);
textEdit = new TextEdit
{
Range = new OmniSharp.Extensions.LanguageServer.Protocol.Models.Range(line, lineLength, line, lineLength),
NewText = fix.replacement
};
}
else
{
textEdit = new TextEdit
{
Range = diagnostic.Range,
NewText = fix.replacement
};
}

return new WorkspaceEdit
{
Changes = new Dictionary<DocumentUri, IEnumerable<TextEdit>>
{
[uri] = new[] { textEdit }
}
};
}

/// <summary>
/// Store line lengths for a document, computed from the document text we already have during scanning.
/// </summary>
public static void SetLineLengths(DocumentUri uri, string text)
{
var lines = text.Split('\n');
var lengths = new int[lines.Length];
for (int i = 0; i < lines.Length; i++)
{
lengths[i] = lines[i].TrimEnd('\r').Length;
}
_lineLengthCache[uri.ToString()] = lengths;
}

private static int GetLineLength(DocumentUri uri, int line)
{
if (_lineLengthCache.TryGetValue(uri.ToString(), out var lengths) && line < lengths.Length)
{
return lengths[line];
}
return 0;
}

/// <summary>
/// Register code fixes for a diagnostic. Called by TextDocumentSyncHandler when processing documents.
/// </summary>
public static void RegisterCodeFix(DocumentUri uri, Diagnostic diagnostic, CodeFixMapping fix)
{
var documentUri = uri.ToString();
var diagnosticKey = CreateDiagnosticKey(documentUri, diagnostic);

var documentFixes = _codeFixCache.GetOrAdd(documentUri, _ => new ConcurrentDictionary<string, List<CodeFixMapping>>());
var fixes = documentFixes.GetOrAdd(diagnosticKey, _ => new List<CodeFixMapping>());

lock (fixes)
{
fixes.Add(fix);
}
}

/// <summary>
/// Clear all code fixes for a document. Called when document is closed or rescanned.
/// </summary>
public static void ClearCodeFixes(DocumentUri uri)
{
_codeFixCache.TryRemove(uri.ToString(), out _);
_lineLengthCache.TryRemove(uri.ToString(), out _);
}

private static string CreateDiagnosticKey(string documentUri, Diagnostic diagnostic)
{
return $"{diagnostic.Code}:{diagnostic.Range.Start.Line}:{diagnostic.Range.Start.Character}:{diagnostic.Range.End.Line}:{diagnostic.Range.End.Character}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ internal static void SetScannerSettings(IConfiguration configuration)
{
fileIgnoreRegexes.Add(new Glob(potentialRegex));
}
catch (Exception e)
catch (Exception)
{
// TODO: Log issue with provided regex
// Invalid glob pattern — skip
}
}
StaticScannerSettings.IgnoreFiles = fileIgnoreRegexes;
Expand All @@ -72,9 +72,9 @@ internal static void SetScannerSettings(IConfiguration configuration)
{
ruleSet.AddPath(path);
}
catch (Exception e)
catch (Exception)
{
// TODO: Log issue with provided path
// Invalid rule path — skip
}
}
ruleSet = ruleSet.WithoutIds(StaticScannerSettings.IgnoreRuleIds);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="0.19.9" />
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
Expand Down
Loading
Loading