/// <summary> /// Excised section from TestPluginCompliance. Tests the specified AssemblyDefinition against all chosen security tests. /// </summary> /// <param name="testConfig">Mirror.</param> /// <param name="asmDef">Mirror.</param> /// <param name="singleResult">Mirror.</param> private static void RunCecilTests(SecurityLevelComplianceTestConfiguration testConfig, AssemblyDefinition asmDef, SecurityLevelComplianceSingleTestResult singleResult) { // Run the tests if (testConfig.RunLevel1Test) { SecurityComplianceSingleCecilTestResult level1TestResults = SecurityComplianceCecilTests.TestLevel1(asmDef); singleResult.TestedLevel1 = true; singleResult.PassLevel1 = level1TestResults.Passed; singleResult.MessagesLevel1.AddRange(level1TestResults.Messages); } if (testConfig.RunLevel2Test) { SecurityComplianceSingleCecilTestResult level2TestResults = SecurityComplianceCecilTests.TestLevel2(asmDef); singleResult.TestedLevel2 = true; singleResult.PassLevel2 = level2TestResults.Passed; singleResult.MessagesLevel2.AddRange(level2TestResults.Messages); } if (testConfig.RunLevel3Test) { SecurityComplianceSingleCecilTestResult level3TestResults = SecurityComplianceCecilTests.TestLevel3(asmDef); singleResult.TestedLevel3 = true; singleResult.PassLevel3 = level3TestResults.Passed; singleResult.MessagesLevel3.AddRange(level3TestResults.Messages); } if (testConfig.RunLevel4Test) { SecurityComplianceSingleCecilTestResult level4TestResults = SecurityComplianceCecilTests.TestLevel4(asmDef); singleResult.TestedLevel4 = true; singleResult.PassLevel4 = level4TestResults.Passed; singleResult.MessagesLevel4.AddRange(level4TestResults.Messages); } }
/// <summary> /// Tests all found PluginFiles against all security levels so as to determine the maximum security level that will allow each plugin to function. /// </summary> /// <param name="terrariaPath">Path to Terraria.exe, which will be referenced by CodeDom during compilation.</param> /// <param name="terrariaDependencyAssemblies">List of Terraria.exe's embedded dependency assemblies, which will be temporarily written to disk and reference by CodeDom during compilation.</param> /// <returns>A SecurityLevelComplianceTestResult object containing the test results.</returns> public static SecurityLevelComplianceTestsResults TestAllSecurityLevelComplianceForAllPlugins(string terrariaPath, List <byte[]> terrariaDependencyAssemblies) { SecurityLevelComplianceTestConfiguration config = new SecurityLevelComplianceTestConfiguration(); config.PluginFilesToTest = new List <PluginFile>(); config.PluginFilesToTest.AddRange(FoundUserPluginFiles); config.TerrariaPath = terrariaPath; config.TerrariaDependencyAssemblies = terrariaDependencyAssemblies; return(SecurityComplianceCecilTests.TestPluginCompliance(config)); }
/// <summary> /// Tests PluginFile(s) against all security levels so as to determine the maximum security level that will allow a plugin to function. /// </summary> /// <param name="testConfig">The parameters to be used in this security level compliance test.</param> /// <returns>A SecurityLevelComplianceTestResult object containing the test results.</returns> public static SecurityLevelComplianceTestsResults TestPluginCompliance(SecurityLevelComplianceTestConfiguration testConfig) { SecurityLevelComplianceTestsResults allResults = new SecurityLevelComplianceTestsResults(); bool firstCompile = true; foreach (PluginFile pluginFile in testConfig.PluginFilesToTest) { SecurityLevelComplianceSingleTestResult singleResult = new SecurityLevelComplianceSingleTestResult(pluginFile); allResults.IndividualResults[pluginFile] = singleResult; try { byte[] asmBytesToTest = null; // If source file, compile it HPluginCompilationResult compileResult = null; if (pluginFile.FileType == PluginFileType.CSSourceFile) { HPluginCompilationConfiguration compileConfig = new HPluginCompilationConfiguration(); compileConfig.SingleAssemblyOutput = true; compileConfig.SourceFiles.Add(pluginFile.PathToFile); compileConfig.UserFilesRootDirectory = testConfig.UserFilesRootDirectory; compileConfig.SingleAssemblyOutput = true; compileConfig.DeleteOutputFilesFromDiskWhenDone = false; // Keep the output files so we can load the PDBs for the cecil-based security tests compileConfig.ReferencesOnDisk.Add(testConfig.TerrariaPath); compileConfig.ReferencesInMemory.AddRange(testConfig.TerrariaDependencyAssemblies); if (firstCompile && testConfig.PluginFilesToTest.Count > 1) // Write the Terraria dependencies to disk on the first compile... { compileConfig.ClearTemporaryFilesWhenDone = false; compileConfig.ReuseTemporaryFiles = false; } else // ...and reuse them on subsequent compiles. They will be finally deleted with HPluginAssemblyCompiler.ClearTemporaryCompileFiles() at the end of these tests. { compileConfig.ClearTemporaryFilesWhenDone = false; compileConfig.ReuseTemporaryFiles = true; } compileResult = HPluginAssemblyCompiler.Compile(compileConfig); if (compileResult.CompiledAssemblies.Count == 0) { // Clean up HPluginAssemblyCompiler.ClearTemporaryCompileFiles(); HPluginAssemblyCompiler.TryRemoveDirectory(compileResult.OutputDirectory); // Note down the compile failure singleResult.CompileFailure = true; if (compileResult.CompileErrors.Count > 0) { singleResult.GenericMessages.Add("Failed to compile source CS files. Error details are as follows."); foreach (CompilerError error in compileResult.CompileErrors) { singleResult.GenericMessages.Add(error.ToString()); } } else { singleResult.GenericMessages.Add("Generic failure while compiling source CS files. No error details are available."); } allResults.AnyCompileFailure = true; } else { // Turn the Assembly into a byte array Assembly asmToTest = compileResult.CompiledAssemblies[0]; asmBytesToTest = asmToTest.ToByteArray(); } } // If already a compiled assembly, load its bytes else if (pluginFile.FileType == PluginFileType.CompiledAssemblyFile) { if (File.Exists(pluginFile.PathToFile)) { try { asmBytesToTest = File.ReadAllBytes(pluginFile.PathToFile); } catch (Exception e) { singleResult.GenericTestFailure = true; singleResult.GenericMessages.Add("Could not load assembly \"" + pluginFile.PathToFile + "\" from disk. Details: " + e); } } } if (asmBytesToTest == null) // Can't test assemblies that failed to load { continue; } ///// Check security compliance with Cecil using (MemoryStream memStream = new MemoryStream(asmBytesToTest)) { // Try to load the compiled assembly's associated pdb file (if one exists) string pdbPath = null; if (pluginFile.FileType == PluginFileType.CSSourceFile) { // Since we used SingleAssemblyOutput and we're only testing one plugin at a time, there should be only one pdb file in the compiler's output. pdbPath = compileResult.OutputFilesOnDisk.Where(x => Path.GetExtension(x) == ".pdb").FirstOrDefault(); } else if (pluginFile.FileType == PluginFileType.CompiledAssemblyFile) { string dllPath = pluginFile.PathToFile; string checkPdbPath = ""; int spot = dllPath.LastIndexOf(".dll"); if (spot > -1) { checkPdbPath = dllPath.Substring(0, spot) + ".pdb"; } if (File.Exists(checkPdbPath)) { pdbPath = checkPdbPath; } } AssemblyDefinition asmDef = null; if (pdbPath != null) { // Run tests with the pdb to get helpful info like line numbers using (FileStream pdbStream = new FileStream(pdbPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { ReaderParameters readerParameters = new ReaderParameters(); readerParameters.ReadSymbols = true; readerParameters.SymbolStream = pdbStream; asmDef = AssemblyDefinition.ReadAssembly(memStream, readerParameters); RunCecilTests(testConfig, asmDef, singleResult); } } else { // Run tests without a pdb and just have generic error messages asmDef = AssemblyDefinition.ReadAssembly(memStream); RunCecilTests(testConfig, asmDef, singleResult); } // Make an aggregated generic messag for the user string overallResult = "Final security level test results >> "; overallResult += "Level 1: " + (singleResult.TestedLevel1 ? (singleResult.PassLevel1 ? "Compliant" : "Violated") : "Untested"); overallResult += "; Level 2: " + (singleResult.TestedLevel2 ? (singleResult.PassLevel2 ? "Compliant" : "Violated") : "Untested"); overallResult += "; Level 3: " + (singleResult.TestedLevel3 ? (singleResult.PassLevel3 ? "Compliant" : "Violated") : "Untested"); overallResult += "; Level 4: " + (singleResult.TestedLevel4 ? (singleResult.PassLevel4 ? "Compliant" : "Violated") : "Untested"); overallResult += "."; singleResult.GenericMessages.Add(overallResult); // All done } // Clear the on-disk output files that were generated during the assembly compile if (compileResult != null && Directory.Exists(compileResult.OutputDirectory)) { DirectoryInfo topDirInfo = new DirectoryInfo(compileResult.OutputDirectory); topDirInfo.Delete(true); } } catch (Exception e) { singleResult.GenericTestFailure = true; singleResult.GenericMessages.Add("Unexpected error during security level testing: " + e.ToString()); } firstCompile = false; } // Delete the temporary disk copies of the Terraria dependencies HPluginAssemblyCompiler.ClearTemporaryCompileFiles(); return(allResults); }