Skip to content

trwalke/MSAL.Analyzer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 

Repository files navigation

MSAL.Analyzer

Analyzer for MSAL.NET

Analyzer logic here

public override void Initialize(AnalysisContext context)
{
/*Issues
How do I get the name and location of the variable in the InvocationExpressionSyntax/MemberAccessExpressionSyntax?
*/
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction((compilationStartContext) =>
{
compilationStartContext.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockContext =>
{
// We only care about method bodies.
if (codeBlockContext.OwningSymbol.Kind != SymbolKind.Method) return;
var method = (IMethodSymbol)codeBlockContext.OwningSymbol;
var CreateMethod = "Create";
var SetBeforeAccessMethod = "SetBeforeAccess";
bool CreateMethodInvocationFound = false;
bool setBeforeOrAfterAccessInvocationFound = false;
MemberAccessExpressionSyntax targetInvocation = null;
codeBlockContext.RegisterSyntaxNodeAction(syntaxNodeContext =>
{
var BuilderType = compilationStartContext?.Compilation?.GetTypeByMetadataName("Microsoft.Identity.Client.ConfidentialClientApplicationBuilder");
var TokenCacheType = compilationStartContext?.Compilation?.GetTypeByMetadataName("Microsoft.Identity.Client.ITokenCache");
//var declaration = syntaxNodeContext.Node as VariableDeclaratorSyntax;
//var x = declaration.DescendantNodes().Where(childNode => (childNode as MemberAccessExpressionSyntax).Name?.Identifier.ValueText == CreateMethod);
var node = syntaxNodeContext.Node as InvocationExpressionSyntax;
if (node == null)
{
return;
}
var expression = node.Expression as MemberAccessExpressionSyntax;
if (expression == null)
{
return;
}
var methodName = expression.Name?.Identifier.ValueText;
if (CreateMethod.Equals(methodName))
{
var nameSyntax = expression.Expression as IdentifierNameSyntax;
if (nameSyntax == null)
{
return;
}
var typeInfo = codeBlockContext?.SemanticModel?.GetTypeInfo(nameSyntax).Type as INamedTypeSymbol;
if (typeInfo?.ConstructedFrom == null)
{
return;
}
if (typeInfo.ConstructedFrom.Equals(BuilderType))
{
CreateMethodInvocationFound = true;
targetInvocation = expression;
return;
}
}
if (SetBeforeAccessMethod.Equals(methodName))
{
var tokenCacheName = expression.DescendantNodes().Where(dNode =>dNode is IdentifierNameSyntax)
.Where(dNode => (dNode as IdentifierNameSyntax).Identifier.Text.Equals("UserTokenCache")).FirstOrDefault();
if (tokenCacheName != null)
{
//var type1 = codeBlockContext.SemanticModel.GetTypeInfo(tokenCacheName).Type as INamedTypeSymbol;
setBeforeOrAfterAccessInvocationFound = true;
}
}
}, SyntaxKind.InvocationExpression);
codeBlockContext.RegisterCodeBlockEndAction(ctx =>
{
if (CreateMethodInvocationFound && !setBeforeOrAfterAccessInvocationFound)
{
ctx.ReportDiagnostic(Diagnostic.Create(Rule, targetInvocation.GetLocation()));
}
CreateMethodInvocationFound = false;
setBeforeOrAfterAccessInvocationFound = false;
return;
});
});
});
}

When running analyzer, MSAL.Analyzer.Vsix must be start up project.

Once new VS instance loads, open Msal.Analyzer.Test to see analyszer running live

.gitignore not configured yet

This analyzer checks for the use of ConfidentialClientApplicationBuilder.Create() reports when it sees that SetBeforeAccess is not used. This is just a POC to see if this will work.

Issues Why does the logic not find the Build() invocation but does find the Create() Why does the analyzer logic loop twice? How do I get the name and location of the variable in the InvocationExpressionSyntax/MemberAccessExpressionSyntax? Why does the analyzer not detect the usage of SetBeforeAccess?

About

Analyzer for MSAL.NET

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published