MethodStreams are virtual files that contain all relevant 'call-flow source-code' for a particular starting methods (note: MethodStreams are one of O2’s biggest innovation on the SAST world)
This is how a MethodStream file is created
- Step 1) Add AST for Method A (the first one)
- Step 2) Add AST for all methods, fields and properties that Method A used/called
- Step 3) recursively executed Step 2) until there is nothing else to do
- Step 4) create the CSharp code for this MethodStream (based on all the ASTs added)
using System; using O2.DotNetWrappers.ExtensionMethods; using CoreLib; namespace CoreLib { public class TM_WebServices { [WebMethod(EnableSession = true)] public bool SetCurrentUserPassword(string password) { return TM_Xml_Database.Current.setCurrentUserPassword(tmAuthentication, password); } public TM_Authentication tmAuthentication { get; set; } } public class TM_UserData_Ex_Users { public static bool setCurrentUserPassword(this TM_Xml_Database tmDb, TM_Authentication tmAuthentication, string password) { var tmUser = tmAuthentication.currentUser; if (tmUser.notNull()) { tmUser.PasswordHash = tmUser.createPasswordHash(password); tmUser.saveTmUser(); return true; } return false; } } public class TMUser_ExtensionMethods { public static string createPasswordHash(this TMUser tmUser, string password) { var sha256Hash = tmUser.UserName.hash_SHA256(password); var passwordHash = sha256Hash.hash_PBKDF2(tmUser.ID); return passwordHash; } } public class SHA256_ExtensionMethods { public static string hash_SHA256(this string text, string salt) { var stringToHash = text + salt; var sha256 = SHA256.Create(); var hashBytes = sha256.ComputeHash(stringToHash.asciiBytes()); var hashString = new StringBuilder(); foreach (byte b in hashBytes) hashString.Append(b.ToString("x2")); return hashString.str(); } } public class PBKDF2_ExtensionMethods { public static int DEFAULT_PBKDF2_BYTES = 64; public static int DEFAULT_PBKDF2_INTERACTIONS = 20000; public static string hash_PBKDF2(this string password, Guid salt) { return password.hash_PBKDF2(salt.str()); } public static string hash_PBKDF2(this string password, string salt) { return password.hash_PBKDF2(salt, DEFAULT_PBKDF2_INTERACTIONS, DEFAULT_PBKDF2_BYTES); } public static string hash_PBKDF2(this string password, string salt, int iterations, int howManyBytes) { var bytes = PBKDF2.GetBytes(password.asciiBytes(), salt.asciiBytes(), iterations, howManyBytes); return bytes.base64Encode(); } } public class PBKDF2 { const byte OPAD = 0x5c; const byte IPAD = 0x36; const int BLOCK_SIZE_IN_BYTES = 64; const int HASH_SIZE_IN_BYTES = 32; public static byte[] GetBytes(string password, byte[] salt, int iterations, int howManyBytes) { return GetBytes(Encoding.UTF8.GetBytes(password), salt, iterations, howManyBytes); } public static byte[] GetBytes(byte[] password, byte[] salt, int iterations, int howManyBytes) { uint cBlocks = (uint)((howManyBytes + HASH_SIZE_IN_BYTES - 1) / HASH_SIZE_IN_BYTES); byte[] saltAndIndex = new byte[salt.Length + 4]; Array.Copy(salt, 0, saltAndIndex, 0, salt.Length); byte[] output = new byte[cBlocks * HASH_SIZE_IN_BYTES]; int outputOffset = 0; SHA256Managed innerHash = new SHA256Managed(); SHA256Managed outerHash = new SHA256Managed(); if (password.Length > BLOCK_SIZE_IN_BYTES) { password = innerHash.ComputeHash(password); } byte[] key = new byte[BLOCK_SIZE_IN_BYTES]; Array.Copy(password, 0, key, 0, password.Length); byte[] InnerKey = new byte[BLOCK_SIZE_IN_BYTES]; byte[] OuterKey = new byte[BLOCK_SIZE_IN_BYTES]; for (int i = 0; i < BLOCK_SIZE_IN_BYTES; ++i) { InnerKey[i] = (byte)(key[i] ^ IPAD); OuterKey[i] = (byte)(key[i] ^ OPAD); } for (int iBlock = 0; iBlock < cBlocks; ++iBlock) { _incrementBigEndianIndex(saltAndIndex, salt.Length); byte[] U = saltAndIndex; for (int i = 0; i < iterations; ++i) { innerHash.Initialize(); innerHash.TransformBlock(InnerKey, 0, BLOCK_SIZE_IN_BYTES, InnerKey, 0); innerHash.TransformFinalBlock(U, 0, U.Length); byte[] temp = innerHash.Hash; outerHash.Initialize(); outerHash.TransformBlock(OuterKey, 0, BLOCK_SIZE_IN_BYTES, OuterKey, 0); outerHash.TransformFinalBlock(temp, 0, temp.Length); U = outerHash.Hash; _xorByteArray(U, 0, HASH_SIZE_IN_BYTES, output, outputOffset); } outputOffset += HASH_SIZE_IN_BYTES; } byte[] result = new byte[howManyBytes]; Array.Copy(output, 0, result, 0, howManyBytes); return result; } static void _incrementBigEndianIndex(byte[] buf, int offset) { unchecked { if (0 == ++buf[offset + 3]) if (0 == ++buf[offset + 2]) if (0 == ++buf[offset + 1]) if (0 == ++buf[offset + 0]) throw new OverflowException(); } } static void _xorByteArray(byte[] src, int srcOffset, int cb, byte[] dest, int destOffset) { int end = checked(srcOffset + cb); while (srcOffset != end) { dest[destOffset] ^= src[srcOffset]; ++srcOffset; ++destOffset; } } } public class TM_UserData_Ex_Users_Persistance { public static TMUser saveTmUser(this TMUser tmUser) { if (TM_Xml_Database.Current.UsingFileStorage) { lock (tmUser) { tmUser.saveAs(tmUser.getTmUserXmlFile()); } } return tmUser; } public static string getTmUserXmlFile(this TMUser tmUser) { return TM_UserData.Current.Path_UserData.pathCombine("{0}.userData.xml".format(tmUser.ID)); } } public class TM_Authentication { public TMUser currentUser { get { try { var tmUser = sessionID.session_TmUser(); if (tmUser.notNull()) tmUser.CSRF_Token = sessionID.str().hash().str(); return tmUser; } catch { return new TMUser(); } } } } public class TMUser { [XmlAttribute()] public string PasswordHash { get; set; } [XmlAttribute()] public string UserName { get; set; } [XmlAttribute()] public Guid ID { get; set; } } public class TM_Xml_Database { public bool UsingFileStorage { get; set; } public static TM_Xml_Database Current { get; set; } } public class TM_UserData { public string Path_UserData { get; set; } public static TM_UserData Current { get; set; } } } namespace O2.DotNetWrappers.ExtensionMethods { public class Object_ExtensionMethods { public static bool notNull(this object _object) { throw new System.Exception("O2 Auto Generated Method"); } } public class Web_ExtensionMethods_Encoding { public static byte[] asciiBytes(this string stringToConvert) { throw new System.Exception("O2 Auto Generated Method"); } public static string base64Encode(this byte[] bytesToEncode) { throw new System.Exception("O2 Auto Generated Method"); } } public class String_ExtensionMethods { public static string str(this object _object) { throw new System.Exception("O2 Auto Generated Method"); } } } namespace System { public class Byte : System.ValueType, System.IComparable, System.IFormattable, System.IConvertible, System.IComparable, System.IEquatable { public string ToString(string format) { throw new System.Exception("O2 Auto Generated Method"); } } public class Int32 : System.ValueType, System.IComparable, System.IFormattable, System.IConvertible, System.IComparable, System.IEquatable { } public class Array : System.ICloneable, System.Collections.IList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable { public int Length { get { throw new NotImplementedException(); } } } }
Here is the GUI that creates any method stream that I want to see:
Here is the script that creates the GUI shown above:
//var topPanel = "{name}".popupWindow(700,400); var topPanel = panel.clear().add_Panel(); var codeViewer = topPanel.insert_Right().add_SourceCodeEditor(); //var textBox = topPanel.add_TextBox(true); var coreLibFolder = @"{localpath}Web Applications\TeamMentor.CoreLib"; var csharpFiles = coreLibFolder.files("*.cs",true); var astData = "astData".o2Cache(()=> { var _astData = new O2MappedAstData(); _astData.loadFiles(csharpFiles); return _astData;}); astData.O2AstResolver.addReference(@"{localpath}\Web Applications\packages\FluentSharp.BCL.5.0.44\lib\net35\FluentSharp.BCL.dll"); astData.O2AstResolver.addReference(@"{localpath}\Web Applications\packages\FluentSharp.CoreLib.5.0.44\lib\net35\FluentSharp.CoreLib.dll"); astData.O2AstResolver.addReference(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Web.dll"); astData.O2AstResolver.addReference(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Web.Services.dll"); var treeView = topPanel.add_TreeView(); treeView.afterSelect<DefaultMethod> ((method)=> { O2Thread.mtaThread( ()=>{ "calculating methodStream for: {0}".info(method.fullName()); var csharpCode = astData.createO2MethodStream(method).csharpCode(); "done".debug(); codeViewer.set_Text(csharpCode, ".cs"); }); }); treeView.add_Nodes(astData.iMethods(), (_iMethod)=>_iMethod.Documentation); treeView.selectFirst(); //using O2.XRules.Database.Languages_and_Frameworks.DotNet //using O2.API.AST.CSharp //using ICSharpCode.SharpDevelop.Dom //using O2.API.AST.ExtensionMethods.CSharp //O2File:O2MappedAstData_ExtensionMethods.cs //O2File:O2MethodStream_ExtensionMethods.cs
Here is simple version (to look at only only file)
//var topPanel = "{name}".popupWindow(700,400); var topPanel = panel.clear().add_Panel(); var codeEditor = topPanel.add_SourceCodeEditor(); var astData = new O2MappedAstData(); var file = @"{localpath}Web Applications\TeamMentor.CoreLib\TM_AppCode\WebServices_REST\Controllers\TM_REST_Libraries.cs"; astData.loadFile(file); codeEditor.open(file); return astData.iMethods().first().fullName(); //using O2.XRules.Database.Languages_and_Frameworks.DotNet //using O2.API.AST.CSharp //using O2.API.AST.ExtensionMethods.CSharp //O2File:O2MappedAstData_ExtensionMethods.cs //O2File:O2MethodStream_ExtensionMethods.cs