基于ANTLR解析树筛选含[HttpPost]但无[ValidateAntiForgeryToken]的C#控制器方法
简便实现C#控制器方法的安全审查(检索带
[HttpPost]但无[ValidateAntiForgeryToken]的方法) 针对你要实现的C#代码自动化安全审查需求,我有几个比转XML用标准XPath更简便的方案,推荐优先考虑微软官方的Roslyn工具集,其次也可以改进你现有的ANTLR实现:
推荐方案:使用Roslyn(原生C#分析器)
Roslyn是.NET平台官方的编译器即服务工具集,相比ANTLR,它对C#语法和特性的支持更原生,处理这类特性检查的场景会直观很多,完全不需要纠结XPath子集的限制,也不用转XML。
实现步骤
- 先引入Roslyn的NuGet包:
Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.CSharp.Workspaces - 编写代码加载源代码或项目,遍历语法树筛选目标方法
示例代码(C#)
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Linq; class AntiForgeryTokenChecker { static void Main(string[] args) { // 加载目标C#文件 var sourceContent = File.ReadAllText(args[0]); var syntaxTree = CSharpSyntaxTree.ParseText(sourceContent); var rootNode = syntaxTree.GetRoot(); // 筛选符合条件的方法:带[HttpPost]、无[ValidateAntiForgeryToken]、位于控制器类中 var targetMethods = rootNode.DescendantNodes() .OfType<MethodDeclarationSyntax>() .Where(method => // 检查是否包含[HttpPost]特性 method.AttributeLists.SelectMany(list => list.Attributes) .Any(attr => attr.Name.ToString() == "HttpPost") && // 检查是否不包含[ValidateAntiForgeryToken]特性 !method.AttributeLists.SelectMany(list => list.Attributes) .Any(attr => attr.Name.ToString() == "ValidateAntiForgeryToken") ) // 过滤控制器类:类名以Controller结尾,或带有[Controller]特性 .Where(method => method.Parent is ClassDeclarationSyntax classDecl && (classDecl.Identifier.Text.EndsWith("Controller") || classDecl.AttributeLists.SelectMany(list => list.Attributes) .Any(attr => attr.Name.ToString() == "Controller")) ); // 输出结果 foreach (var method in targetMethods) { var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; Console.WriteLine($"找到目标方法:{method.Identifier.Text},位于第{lineNumber}行,所属类:{(method.Parent as ClassDeclarationSyntax)?.Identifier.Text}"); } } }
为什么选Roslyn?
- 原生支持C#所有语法和特性,不会出现ANTLR可能存在的语法兼容性问题
- 提供强类型的语法节点,遍历和检查逻辑更易读、易维护
- 支持分析整个项目而非单个文件,适合大规模代码审查场景
改进你的ANTLR实现(无需转XML)
如果你坚持使用ANTLR,可以通过手动遍历ParseTree的方式实现检查,避开ANTLR XPath子集的限制:
修改后的Java代码
import java.io.*; import java.util.*; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; import org.antlr.v4.runtime.tree.xpath.*; public class MyParser { public static void main(String[] args) throws IOException { CharStream input = CharStreams.fromFileName(args[0]); Lexer lexer = new CSharpLexer(input); TokenStream stream = new CommonTokenStream(lexer); CSharpParser parser = new CSharpParser(stream); ParseTree tree = parser.compilation_unit(); // 遍历所有类声明 Collection<ParseTree> classDeclarations = XPath.findAll(tree, "//class_declaration", parser); for (ParseTree classNode : classDeclarations) { CSharpParser.Class_declarationContext classCtx = (CSharpParser.Class_declarationContext) classNode; String className = classCtx.identifier().getText(); // 简单判断是否为控制器类(可根据实际需求扩展,比如检查[Controller]特性) if (!className.endsWith("Controller")) { continue; } // 遍历当前类中的所有方法 Collection<ParseTree> methodDeclarations = XPath.findAll(classNode, ".//method_declaration", parser); for (ParseTree methodNode : methodDeclarations) { CSharpParser.Method_declarationContext methodCtx = (CSharpParser.Method_declarationContext) methodNode; boolean hasHttpPost = false; boolean hasAntiForgeryToken = false; // 检查方法的所有特性 if (methodCtx.attribute_lists() != null) { for (CSharpParser.Attribute_listContext attrList : methodCtx.attribute_lists().attribute_list()) { for (CSharpParser.AttributeContext attr : attrList.attribute()) { String attrName = attr.attribute_name().getText(); if ("HttpPost".equals(attrName)) { hasHttpPost = true; } else if ("ValidateAntiForgeryToken".equals(attrName)) { hasAntiForgeryToken = true; } } } } // 输出符合条件的方法 if (hasHttpPost && !hasAntiForgeryToken) { System.out.printf("找到目标方法:%s,所属控制器类:%s%n", methodCtx.identifier().getText(), className); } } } } }
关键注意点
- 需要将ANTLR的ParseTree节点强转为对应的Context类(比如
CSharpParser.Method_declarationContext),才能访问特性列表等具体子节点 - 示例中的控制器判断是简单的类名后缀检查,你可以根据实际项目规则扩展(比如检查类是否带有
[Controller]特性)
内容的提问来源于stack exchange,提问作者Sjoerd
相关产品推荐
相关产品推荐

