如何检测C#中Action方法传递给函数的非白名单变量?
实现Action方法参数白名单检测的测试函数
需求概述
我们需要编写测试逻辑,验证Action方法中传递给外部函数的变量是否都通过CustomAttribute列入了白名单。如果存在未授权的变量(比如示例中的myVar1)被传递,测试需返回失败(false);仅白名单内的变量(如myParam1)允许传递。
实现方案:基于Roslyn静态分析
利用Roslyn解析代码语法树,直接分析Action方法的结构、特性标记和函数调用行为,是最可靠的静态检测方式。以下是完整的测试实现:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.Linq; using Xunit; public class ActionWhitelistValidationTests { [Fact] public void MyActionMethod_Fails_WhenPassingNonWhitelistedVariables() { // 1. 定义目标代码(实际项目中可直接加载源码文件或编译单元) var targetCode = @" using System.Threading.Tasks; using System.Web.Http; public class SampleController : ApiController { private readonly IDataService _dataService; public SampleController(IDataService dataService) => _dataService = dataService; [CustomAttribute(""myParam1"")] public virtual async Task<IHttpActionResult> MyActionMethod(string myParam1, int myParam2) { var myVar1 = ""sensitive-value""; return await _dataService.GetData(myParam1, myVar1); } } public interface IDataService { Task<IHttpActionResult> GetData(string allowedParam, string restrictedParam); } public class CustomAttribute : Attribute { public CustomAttribute(string whitelistedParam) => ParamName = whitelistedParam; public string ParamName { get; } }"; // 2. 构建语法树与编译环境 var syntaxTree = CSharpSyntaxTree.ParseText(targetCode); var compilation = CSharpCompilation.Create("WhitelistTestCompilation") .AddReferences( MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location), MetadataReference.CreateFromFile(typeof(Task).Assembly.Location), MetadataReference.CreateFromFile(typeof(IHttpActionResult).Assembly.Location)) .AddSyntaxTrees(syntaxTree); // 3. 定位目标Action方法的语法节点 var targetMethod = syntaxTree.GetRoot() .DescendantNodes() .OfType<MethodDeclarationSyntax>() .First(m => m.Identifier.Text == "MyActionMethod"); // 4. 提取CustomAttribute标记的白名单参数 var whitelistedParams = targetMethod.AttributeLists .SelectMany(list => list.Attributes) .Where(attr => attr.Name.ToString() == "CustomAttribute") .SelectMany(attr => attr.ArgumentList.Arguments) .Select(arg => arg.Expression.ToString().Trim('"')) .ToList(); // 5. 收集所有对外函数调用的参数 var invokedArguments = targetMethod.DescendantNodes() .OfType<InvocationExpressionSyntax>() .SelectMany(invocation => invocation.ArgumentList.Arguments) .Select(arg => arg.Expression.ToString()) .ToList(); // 6. 筛选出未授权的传递变量 var actionParamNames = targetMethod.ParameterList.Parameters.Select(p => p.Identifier.Text).ToList(); var unauthorizedArguments = invokedArguments .Where(arg => // 两种违规情况:1. Action参数但不在白名单;2. 非Action参数(如局部变量) (actionParamNames.Contains(arg) && !whitelistedParams.Contains(arg)) || !actionParamNames.Contains(arg)) .Distinct() .ToList(); // 7. 断言:无未授权变量则测试通过,否则失败 Assert.Empty(unauthorizedArguments); } }
代码说明
- 语法树解析:通过Roslyn将目标代码解析为语法树,方便后续分析方法结构和调用逻辑
- 白名单提取:从Action方法的
CustomAttribute中提取允许传递的参数名 - 调用参数收集:遍历方法内所有函数调用,记录传递的变量名称
- 违规检测:对比传递的变量,判断是否属于未授权的情况(局部变量或未标记的Action参数)
- 结果断言:如果存在违规变量,
Assert.Empty会触发测试失败,符合需求
示例验证
在示例代码中:
myParam1在白名单内,传递给GetData是合法的myVar1是局部变量,未被CustomAttribute标记,属于违规传递,测试会失败(返回false)
内容的提问来源于stack exchange,提问作者Mr. Flibble
相关产品推荐
相关产品推荐

