Last active
March 10, 2017 21:19
-
-
Save vbfox/6e835299e6e941434b1b9b0a187768ad to your computer and use it in GitHub Desktop.
Security for @fsibot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#r "../packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.dll" | |
open System | |
open System.IO | |
open System.Threading | |
open System.Threading.Tasks | |
open System.Net | |
open Microsoft.FSharp.Compiler | |
open Microsoft.FSharp.Compiler.Ast | |
open Microsoft.FSharp.Compiler.Interactive.Shell | |
open Microsoft.FSharp.Compiler.SourceCodeServices | |
let checker = FSharpChecker.Create(keepAssemblyContents=true) | |
module TypeUsage = | |
module Extraction = | |
let listGuarded f = | |
try | |
f () | |
with | |
| _ -> [] | |
/// Extract entities from an entity (It can be an abreviation for a type) | |
let rec extractFromEntity (e: FSharpEntity): FSharpEntity list = | |
if e.IsFSharpAbbreviation then | |
extractFromType e.AbbreviatedType | |
else | |
[e] | |
/// Extract entities from a type "string list option" would give String, list<'a> and option<'a> | |
and extractFromType (t: FSharpType): FSharpEntity list = | |
if t.IsAbbreviation then | |
extractFromType t.AbbreviatedType | |
else if t.IsFunctionType then | |
extractFromTypeGenericArguments t | |
else | |
(if t.HasTypeDefinition then extractFromEntity t.TypeDefinition else []) | |
@ (extractFromTypeGenericArguments t) | |
/// Extract entities from type arguments | |
/// (Tuple members & function type parameters are considered generic arguments) | |
and extractFromTypeGenericArguments (t: FSharpType): FSharpEntity list = | |
listGuarded (fun () -> t.GenericArguments |> List.ofSeq ) |> List.collect extractFromType | |
/// Get entities for the types containing the member | |
/// (Return both where they are implemented and what type they extends for extension methods) | |
let getEnclosingEntities (f: FSharpMemberOrFunctionOrValue) = | |
listGuarded (fun () -> [f.EnclosingEntity]) | |
@ | |
listGuarded (fun () -> [f.LogicalEnclosingEntity]) | |
let rec extractFromExpr (expr: FSharpExpr) = | |
// Guard against: FSharp.Compiler.Service cannot yet return this kind of pattern match | |
let entitiesFromExpr = listGuarded (fun () -> | |
match expr with | |
| BasicPatterns.Call (_,f,_,_,_) -> getEnclosingEntities f | |
| BasicPatterns.Value f -> getEnclosingEntities f | |
| BasicPatterns.Lambda (f,_) -> getEnclosingEntities f | |
| BasicPatterns.NewObject (f,_,_) -> getEnclosingEntities f | |
| BasicPatterns.LetRec (f,_) -> f |> List.collect (fun (f, _) -> getEnclosingEntities f) | |
| BasicPatterns.Let ((f,_),_) -> getEnclosingEntities f | |
| BasicPatterns.ValueSet (f,_) -> getEnclosingEntities f | |
| BasicPatterns.TryWith (_,f,_,f2,_) -> (getEnclosingEntities f) @ (getEnclosingEntities f2) | |
| _ -> []) | |
// Guard against: FSharp.Compiler.Service cannot yet return this kind of pattern match | |
let subExpr = listGuarded (fun () -> expr.ImmediateSubExpressions) | |
extractFromType expr.Type | |
@ (entitiesFromExpr |> List.collect extractFromEntity) | |
@ (subExpr |> List.collect extractFromExpr) | |
let rec extractFromDecl decl = | |
match decl with | |
| FSharpImplementationFileDeclaration.Entity (e, subDecls) -> | |
subDecls |> List.collect extractFromDecl | |
| FSharpImplementationFileDeclaration.MemberOrFunctionOrValue(v, vs, e) -> | |
extractFromExpr e | |
| FSharpImplementationFileDeclaration.InitAction(e) -> | |
extractFromExpr e | |
let extractFromFile (f :FSharpImplementationFileContents) = | |
f.Declarations |> List.collect extractFromDecl | |
let writeAllTextAsync path (content: string) = async { | |
use s = File.OpenWrite(path) | |
use w = new StreamWriter(s) | |
do! w.WriteAsync(content) |> Async.AwaitTask | |
} | |
let parseAndCheckCode input = async { | |
let filePath = System.IO.Path.GetTempFileName() | |
File.Delete(filePath) | |
let filePath = Path.ChangeExtension(filePath, "fsx") | |
do! writeAllTextAsync filePath input | |
let! projOptions = checker.GetProjectOptionsFromScript(filePath, input) | |
let! result = checker.ParseAndCheckProject(projOptions) | |
File.Delete(filePath) | |
return result | |
} | |
let extractTypesFromCode input = async { | |
let! checkProjectResults = parseAndCheckCode input | |
let checkedFile = checkProjectResults.AssemblyContents.ImplementationFiles |> List.tryHead | |
match checkedFile with | |
| Some checkedFile -> return Extraction.extractFromFile checkedFile |> List.distinct | |
| None -> return [] | |
} | |
let extractTypesFullNamesFromCode input = async { | |
let! types = extractTypesFromCode input | |
return types |> List.choose (fun t -> t.TryFullName) | |
} | |
let doTest s = | |
TypeUsage.extractTypesFullNamesFromCode s |> Async.RunSynchronously |> List.sort | |
doTest """ | |
type a = System .Reflection.Assembly | |
type w = System .Net.WebClient | |
a.Load((new w()).DownloadData("https://google.com/")) | |
""" | |
// ["System.Reflection.Assembly"; "System.Byte"; "System.Net.WebClient"; "System.String"] | |
doTest """ | |
System.IO.File.ReadAllLines("C:\foo") | |
""" | |
// ["System.String"; "System.IO.File"] | |
doTest """ | |
[1..255] |> List.filter (fun x -> (x % 3) = 0) | |
""" | |
// ["Microsoft.FSharp.Collections.FSharpList`1"; "System.Int32"; | |
// "Microsoft.FSharp.Core.Operators"; "Microsoft.FSharp.Collections.SeqModule"; | |
// "System.Collections.Generic.IEnumerable`1"; "System.Boolean"; | |
// "Microsoft.FSharp.Collections.ListModule"] | |
doTest """ | |
(System.Type.GetType("System"+".Environment").GetMethods().[15]).Invoke(null, [|"fsibotserverless_STORAGE"|]) | |
""" | |
// ["System.Object"; "System.Reflection.MethodBase"; "System.Reflection.MethodInfo"; | |
// "Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions"; | |
// "System.Type"; "System.String"; "Microsoft.FSharp.Core.Operators"; "System.Int32"] | |
doTest """ | |
let rec f y r=printfn "20%i\t%f" y r;if y<70 then f (y+1) ((r+if y<29 then 7.9e3 elif y<35 then 1.68e4 else 1.78e4)*1.00847) | |
f 16 0. | |
""" | |
// ["Microsoft.FSharp.Core.Unit"; "System.Int32"; "Microsoft.FSharp.Core.float`1"; | |
// "Microsoft.FSharp.Core.CompilerServices.MeasureOne"; "Microsoft.FSharp.Core.ExtraTopLevelOperators"; | |
// "Microsoft.FSharp.Core.PrintfFormat`4"; "System.IO.TextWriter"; | |
// "Microsoft.FSharp.Core.PrintfFormat`5"; "System.String"; "System.Boolean"; | |
// "Microsoft.FSharp.Core.Operators"; "TmpC4B7"] | |
let txt = File.ReadAllText(Path.Combine(__SOURCE_DIRECTORY__, "security.fsx")) | |
doTest txt | |
// ["Microsoft.FSharp.Collections.FSharpList`1"; | |
// "Microsoft.FSharp.Collections.FSharpSet`1"; | |
// "Microsoft.FSharp.Collections.ListModule"; | |
// "Microsoft.FSharp.Collections.SeqModule"; | |
// "Microsoft.FSharp.Collections.SetModule"; | |
// "Microsoft.FSharp.Core.FSharpOption`1"; | |
// "Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions"; | |
// "Microsoft.FSharp.Core.Operators"; "Microsoft.FSharp.Core.OptionModule"; | |
// "Microsoft.FSharp.Core.Unit"; "System.Boolean"; "System.Char"; | |
// "System.Collections.Generic.IEnumerable`1"; | |
// "System.Collections.IEqualityComparer"; "System.Int32"; "System.Object"; | |
// "System.String"; "TmpB55C"; "TmpB55C.Blocked"] | |
doTest "type Blocked = |Foo |Bar" | |
// ["Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions"; | |
// "Microsoft.FSharp.Core.Operators"; "System.Boolean"; | |
// "System.Collections.IEqualityComparer"; "System.Int32"; "System.Object"; | |
// "Tmp7B7D.Blocked"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment