Skip to content

Instantly share code, notes, and snippets.

@vbfox
Last active March 10, 2017 21:19
Show Gist options
  • Save vbfox/6e835299e6e941434b1b9b0a187768ad to your computer and use it in GitHub Desktop.
Save vbfox/6e835299e6e941434b1b9b0a187768ad to your computer and use it in GitHub Desktop.
Security for @fsibot
#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