Skip to content

Commit 7242b68

Browse files
author
Jimmy Byrd
authored
WIP Working analyzers (#1)
Make work with MiniScaffold
1 parent 89e6866 commit 7242b68

File tree

12 files changed

+278
-213
lines changed

12 files changed

+278
-213
lines changed

.github/build.yml

Lines changed: 0 additions & 35 deletions
This file was deleted.

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
matrix:
1010
os: [ubuntu-latest, windows-latest, macOS-latest]
11-
dotnet: [3.1.100]
11+
dotnet: [3.1.200]
1212
runs-on: ${{ matrix.os }}
1313

1414
steps:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,6 @@ coverage.*.xml
266266
# Paket tool store
267267
.paket/.store
268268
.paket/paket
269+
270+
.fake
271+
.ionide

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ language: csharp
22
sudo: required
33
dist: xenial
44

5-
dotnet: 3.0.100
5+
dotnet: 3.0.200
66
mono:
77
- latest # => "stable release"
88
- alpha

paket.dependencies

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
source https://www.nuget.org/api/v2
22
source https://api.nuget.org/v3/index.json
3+
source paket-files/github.com/fsharp/FsAutoComplete/lib
34
storage: none
45
clitool dotnet-mono 0.5.2
6+
7+
git https://github.com/fsharp/FsAutoComplete.git master build:"build.cmd", Packages: /bin/pkgs/, OS: win
8+
git https://github.com/fsharp/FsAutoComplete.git master build:"build.sh", Packages: /bin/pkgs/, OS: osx
9+
git https://github.com/fsharp/FsAutoComplete.git master build:"build.sh", Packages: /bin/pkgs/, OS: linux
10+
11+
nuget ProjectSystem
12+
513
nuget FSharp.Core
614
nuget Microsoft.SourceLink.GitHub prerelease copy_local: true
715
nuget Microsoft.NETFramework.ReferenceAssemblies copy_local: true
816
nuget Expecto 8.13.1
917
nuget YoloDev.Expecto.TestSdk 0.8.0
1018
nuget Microsoft.NET.Test.Sdk 15.7.2
1119
nuget altcover ~> 6
20+
nuget FSharp.Compiler.Service 34.1.1
1221
nuget FSharp.Analyzers.SDK 0.4.0
1322

1423
// [ FAKE GROUP ]

paket.lock

Lines changed: 118 additions & 90 deletions
Large diffs are not rendered by default.

src/BinaryDefense.FSharp.Analyzers.Hashing/Library.fs

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
namespace BinaryDefense.FSharp.Analyzers
2-
2+
open System.Security.Cryptography
3+
open System.Text
34
module Hashing =
45
open System
56
open FSharp.Analyzers.SDK
67
open FSharp.Compiler.SourceCodeServices
78
open FSharp.Compiler.Range
89

10+
911
let rec visitExpr memberCallHandler (e:FSharpExpr) =
1012
// printfn "e -> %A" e
1113
match e with
@@ -152,46 +154,68 @@ module Hashing =
152154
// ()
153155
// #endif
154156

157+
let forTheReflectionGods () =
158+
use sha1 = SHA1.Create()
159+
sha1.ComputeHash(UTF8Encoding().GetBytes("foo"))
160+
155161
let matchers =
156162
[
157163
"System.Security.Cryptography.MD5CryptoServiceProvider", MD5
158164
"System.Security.Cryptography.MD5.Create", MD5
159165
"System.Security.Cryptography.SHA1CryptoServiceProvider", SHA1
160-
"System.Security.Cryptography.SHA1.Create", SHA1
166+
(sprintf "%s.Create" typedefof<SHA1>.FullName), SHA1
161167
] |> dict
162168

169+
// forTheReflectionGods () |> printfn "%A"
163170
[<Analyzer>]
164171
let weakHashingAnalyzer : Analyzer =
165172
// waitForDebuggerAttached ("App")
166173
fun ctx ->
167-
let state = ResizeArray<WeakHash * range>()
168-
let handler (range: range) (m: FSharpMemberOrFunctionOrValue) =
169-
// printfn "%A" m
170-
// printfn "%A" m.FullTypeSafe
171-
let name =
172-
if m.DeclaringEntity.IsSome then
173-
String.Join(".", m.DeclaringEntity.Value.FullName, m.DisplayName)
174-
else
175-
m.FullTypeSafe.Value.Format(FSharpDisplayContext.Empty)
176-
// printfn "name -> %s" name
174+
if ctx.FileName.EndsWith("AssemblyInfo.fs") then
175+
[]
176+
else
177+
let state = ResizeArray<WeakHash * range>()
178+
let handler (range: range) (m: FSharpMemberOrFunctionOrValue) =
179+
180+
// printfn "DeclaringEntity --> %A" m.DeclaringEntity
181+
// printfn "TryGetFullDisplayName -> %A" <| m.TryGetFullDisplayName()
182+
// printfn "FullTypeSafe -> %A" m.FullTypeSafe
183+
// m.FullTypeSafe
184+
// |> Option.iter(fun fts ->
185+
// try
186+
// printfn "FullTypeSafe.IsUnresolved -> %A" fts.IsUnresolved
187+
// printfn "FullTypeSafe.TypeDefinition -> %A" fts.TypeDefinition
188+
// printfn "FullTypeSafe.TypeDefinition -> %A" fts.TypeDefinition.DeclaredInterfaces
189+
// with e -> eprintfn "%A" e
190+
// // fts.TypeDefinition.
191+
// )
192+
// let v = m.
193+
// v.
194+
// printfn "%A"
195+
let name =
196+
if m.DeclaringEntity.IsSome then
197+
String.Join(".", m.DeclaringEntity.Value.FullName, m.DisplayName)
198+
else
199+
m.FullTypeSafe.Value.Format(FSharpDisplayContext.Empty)
200+
// printfn "name -> %s" name
177201

178-
match
179-
matchers
180-
|> Seq.tryFind(fun k -> name.Contains(k.Key))
181-
with
182-
| Some v ->
183-
state.Add (v.Value, range)
184-
| _ -> ()
185-
// printfn "ctx.TypedTree.Declarations --> %A" ctx.TypedTree.Declarations
186-
ctx.TypedTree.Declarations |> List.iter (visitDeclaration handler)
187-
state
188-
|> Seq.map (fun (hash, r) ->
189-
{ Type = "Weak hashing analyzer"
190-
Message = sprintf "%A shouldn't be used. Consider changing to SHA256 or SHA512." hash
191-
Code = toCode hash
192-
Severity = Warning
193-
Range = r
194-
Fixes = []}
202+
match
203+
matchers
204+
|> Seq.tryFind(fun k -> name.Contains(k.Key))
205+
with
206+
| Some v ->
207+
state.Add (v.Value, range)
208+
| _ -> ()
209+
// printfn "ctx.TypedTree.Declarations --> %A" ctx.TypedTree.Declarations
210+
ctx.TypedTree.Declarations |> List.iter (visitDeclaration handler)
211+
state
212+
|> Seq.map (fun (hash, r) ->
213+
{ Type = "Weak hashing analyzer"
214+
Message = sprintf "%A shouldn't be used. Consider changing to SHA256 or SHA512." hash
215+
Code = toCode hash
216+
Severity = Warning
217+
Range = r
218+
Fixes = []}
195219

196-
)
197-
|> Seq.toList
220+
)
221+
|> Seq.toList

tests/BinaryDefense.FSharp.Analyzers.Tests/Analyzer.fs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ open System.IO
55
open FSharp.Compiler.SourceCodeServices
66
open FSharp.Compiler.Text
77
open FSharp.Analyzers.SDK
8+
open ProjectSystem
9+
810
let checker =
911
FSharpChecker.Create(
1012
projectCacheSize = 200,
1113
keepAllBackgroundResolutions = true,
1214
keepAssemblyContents = true,
1315
ImplicitlyStartBackgroundWork = true)
1416

17+
let projectSystem = ProjectController(checker)
1518

1619
let dumpOpts (opts : FSharpProjectOptions) =
1720
printfn "FSharpProjectOptions.OtherOptions ->"
@@ -23,6 +26,29 @@ let toAbsolutePath path =
2326
FileInfo(path).FullName
2427

2528
let loadProject file =
29+
async {
30+
let! projLoading = projectSystem.LoadProject file ignore FSIRefs.TFM.NetCore (fun _ _ _ -> ())
31+
let filesToCheck =
32+
match projLoading with
33+
| ProjectResponse.Project proj ->
34+
// printInfo "Project %s loaded" file
35+
proj.projectFiles
36+
|> List.choose (fun file ->
37+
projectSystem.GetProjectOptions file
38+
|> Option.map (fun opts -> file, opts)
39+
)
40+
|> Some
41+
| ProjectResponse.ProjectError(errorDetails) ->
42+
printfn "Project loading failed: %A" errorDetails
43+
None
44+
| ProjectResponse.ProjectLoading(_)
45+
| ProjectResponse.WorkspaceLoad(_) ->
46+
None
47+
48+
return filesToCheck
49+
} |> Async.RunSynchronously
50+
51+
let loadFile file =
2652
async {
2753
let! source = IO.File.ReadAllTextAsync file |> Async.AwaitTask
2854
let! (opts, error) = checker.GetProjectOptionsFromScript(file, SourceText.ofString source, assumeDotNetFramework = false, useSdkRefs = true, useFsiAuxLib = true, otherFlags = [|"--targetprofile:netstandard" |])
@@ -38,7 +64,7 @@ let loadProject file =
3864
else
3965
i
4066
)
41-
// dumpOpts opts
67+
dumpOpts opts
4268
return file,{ opts with OtherOptions = newOO}
4369
} |> Async.RunSynchronously
4470

@@ -94,21 +120,25 @@ let createContext (file, text: string, p: FSharpParseFileResults,c: FSharpCheckF
94120
Some context
95121
| _ -> None
96122

123+
type Loader =
124+
| Project of string
125+
| File of string
97126

98-
let runProject proj (analyzers : Analyzer seq) =
99-
let path =
100-
Path.Combine(Environment.CurrentDirectory, proj)
101-
|> Path.GetFullPath
102-
103-
let file =
104-
loadProject path
105-
|> typeCheckFile
106-
|> Option.bind createContext
127+
let runProject (proj : Loader) (analyzers : Analyzer seq) =
128+
// let path =
129+
// Path.Combine(Environment.CurrentDirectory, proj)
130+
// |> Path.GetFullPath
107131

132+
let files =
133+
match proj with
134+
| Project f -> loadProject f
135+
| File f -> Some [ loadFile f ]
136+
|> Option.map(List.choose typeCheckFile)
137+
|> Option.map(List.choose createContext)
108138

109-
file
110-
|> Option.map(fun ctx ->
139+
files
140+
|> Option.map(List.collect(fun ctx ->
111141
analyzers
112142
|> Seq.collect (fun analyzer -> analyzer ctx)
113143
|> Seq.toList
114-
)
144+
))

tests/BinaryDefense.FSharp.Analyzers.Tests/BinaryDefense.FSharp.Analyzers.Tests.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</ItemGroup>
1313
<ItemGroup>
1414
<ProjectReference Include="../../src/BinaryDefense.FSharp.Analyzers.Hashing/BinaryDefense.FSharp.Analyzers.Hashing.fsproj" />
15-
<ProjectReference Include="..\examples\hashing\hashing.fsproj" />
15+
<!-- <ProjectReference Include="..\examples\hashing\hashing.fsproj" /> -->
1616
</ItemGroup>
1717
<Import Project="..\..\.paket\Paket.Restore.targets" />
1818
</Project>

tests/BinaryDefense.FSharp.Analyzers.Tests/Tests.fs

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,49 @@ let testSeq name tests = testList name (List.ofSeq tests)
1212
let tests =
1313
testSeq "Hashing Tests" <|
1414
testParam analyzers [
15-
"Check MD5.Create() binding", fun analyzers () ->
16-
let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/md5create.fs")
17-
let results = (AnalyzerBootstrap.runProject file analyzers).Value
18-
Expect.hasLength results 1 ""
19-
results
20-
|> Seq.iter(fun r ->
21-
Expect.stringContains r.Message "MD5" ""
22-
)
23-
"Check MD5CryptoServiceProvider binding", fun analyzers () ->
24-
let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/md5CryptoServicebinding.fs")
25-
let results = (AnalyzerBootstrap.runProject file analyzers).Value
26-
Expect.hasLength results 4 ""
27-
results
28-
|> Seq.iter(fun r ->
29-
Expect.stringContains r.Message "MD5" ""
30-
)
31-
"Check SHA1.Create() binding", fun analyzers () ->
32-
let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1create.fs")
33-
let results = (AnalyzerBootstrap.runProject file analyzers).Value
34-
Expect.hasLength results 1 ""
35-
results
36-
|> Seq.iter(fun r ->
37-
Expect.stringContains r.Message "SHA1" ""
38-
)
39-
"Check SHACryptoServiceProvider binding", fun analyzers () ->
40-
let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1CryptoServicebinding.fs")
41-
let results = (AnalyzerBootstrap.runProject file analyzers).Value
42-
Expect.hasLength results 4 ""
43-
results
44-
|> Seq.iter(fun r ->
45-
Expect.stringContains r.Message "SHA1" ""
46-
)
47-
"Check SHACryptoServiceProvider ctor", fun analyzers () ->
48-
let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1CryptoServicector.fs")
49-
let results = (AnalyzerBootstrap.runProject file analyzers).Value
50-
Expect.hasLength results 1 ""
51-
results
52-
|> Seq.iter(fun r ->
53-
Expect.stringContains r.Message "SHA1" ""
54-
)
15+
// "Check MD5.Create() binding", fun analyzers () ->
16+
// let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/md5create.fs")
17+
// let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.File file) analyzers).Value
18+
// Expect.hasLength results 1 ""
19+
// results
20+
// |> Seq.iter(fun r ->
21+
// Expect.stringContains r.Message "MD5" ""
22+
// )
23+
// "Check MD5CryptoServiceProvider binding", fun analyzers () ->
24+
// let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/md5CryptoServicebinding.fs")
25+
// let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.File file) analyzers).Value
26+
// Expect.hasLength results 4 ""
27+
// results
28+
// |> Seq.iter(fun r ->
29+
// Expect.stringContains r.Message "MD5" ""
30+
// )
31+
// "Check SHA1.Create() binding", fun analyzers () ->
32+
// let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1create.fs")
33+
// let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.File file) analyzers).Value
34+
// Expect.hasLength results 1 ""
35+
// results
36+
// |> Seq.iter(fun r ->
37+
// Expect.stringContains r.Message "SHA1" ""
38+
// )
39+
// "Check SHACryptoServiceProvider binding", fun analyzers () ->
40+
// let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1CryptoServicebinding.fs")
41+
// let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.File file) analyzers).Value
42+
// Expect.hasLength results 4 ""
43+
// results
44+
// |> Seq.iter(fun r ->
45+
// Expect.stringContains r.Message "SHA1" ""
46+
// )
47+
// "Check SHACryptoServiceProvider ctor", fun analyzers () ->
48+
// let file = IO.Path.Combine(__SOURCE_DIRECTORY__ ,"../examples/hashing/sha1CryptoServicector.fs")
49+
// let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.File file) analyzers).Value
50+
// Expect.hasLength results 1 ""
51+
// results
52+
// |> Seq.iter(fun r ->
53+
// Expect.stringContains r.Message "SHA1" ""
54+
// )
55+
"Check project", fun analyzers () ->
56+
let file = IO.Path.Combine(__SOURCE_DIRECTORY__, "../examples/hashing/hashing.fsproj")
57+
// let file = "/Users/jimmybyrd/Documents/GitHub/MiniScaffold/Content/Library/src/MyLib.1/MyLib.1.fsproj"
58+
let results = (AnalyzerBootstrap.runProject (AnalyzerBootstrap.Project file) analyzers).Value
59+
Expect.isGreaterThan results.Length 0 ""
5560
]

0 commit comments

Comments
 (0)