Skip to content

Commit

Permalink
really for real go static analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
csasarak committed Dec 3, 2024
1 parent 4cc7bf5 commit 1847732
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# FOSSA CLI Changelog

## 3.9.41
- GoModules: Fix a bug where a static analysis method was not exposed. ([#1468](https://github.com/fossas/fossa-cli/pull/1486))

## 3.9.40
- Licensing: Fix a bug where license scanner output sometimes included log lines, which breaks JSON parsing

Expand Down
9 changes: 9 additions & 0 deletions src/Strategy/Go/Gomod.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Strategy.Go.Gomod (
PackageVersion (..),
parsePackageVersion,
gomodParser,
analyzeStatic,
) where

import Control.Algebra (Has)
Expand Down Expand Up @@ -396,6 +397,14 @@ analyze' file = do
pure ()
pure (graph, Partial)

-- | This variant of analyze will not attempt to fill in transitive dependencies.
analyzeStatic :: (Has ReadFS sig m, Has Diagnostics sig m) => Path Abs File -> m (Graphing Dependency, GraphBreadth)
analyzeStatic file = do
graph <- graphingGolang $ do
gomod <- readContentsParser gomodParser file
context "Building dependency graph (static)" $ buildGraph gomod
pure (graph, Partial)

buildGraph :: Has GolangGrapher sig m => Gomod -> m ()
buildGraph = traverse_ go . resolve
where
Expand Down
33 changes: 20 additions & 13 deletions src/Strategy/Gomodules.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import App.Fossa.Analyze.Types (AnalyzeProject (analyzeProjectStaticOnly), analy
import App.Fossa.Config.Analyze (ExperimentalAnalyzeConfig (useV3GoResolver), GoDynamicTactic (..))
import App.Util (guardStrictMode)
import Control.Carrier.Diagnostics (warn)
import Control.Effect.Diagnostics (Diagnostics, context, fatalText, recover, (<||>))
import Control.Effect.Diagnostics (Diagnostics, context, recover, (<||>))
import Control.Effect.Reader (Reader, ask, asks)
import Control.Monad (when)
import Data.Aeson (ToJSON)
Expand Down Expand Up @@ -58,7 +58,7 @@ instance ToJSON GomodulesProject

instance AnalyzeProject GomodulesProject where
analyzeProject _ proj = asks useV3GoResolver >>= getDeps proj
analyzeProjectStaticOnly _ proj = staticAnalysisResults proj <$> staticAnalysis proj
analyzeProjectStaticOnly _ = staticAnalysis

mkProject :: GomodulesProject -> DiscoveredProject GomodulesProject
mkProject project =
Expand All @@ -72,7 +72,9 @@ mkProject project =
getDeps :: (GetDepsEffs sig m) => GomodulesProject -> GoDynamicTactic -> m DependencyResults
getDeps project goDynamicTactic = do
mode <- ask
(graph, graphBreadth) <- context "Gomodules" $ dynamicAnalysis <||> guardStrictMode mode (staticAnalysis project)
(graph, graphBreadth) <-
context "Gomodules" $
dynamicAnalysis <||> guardStrictMode mode fallbacks
stdlib <- recover . context "Collect go standard library information" . listGoStdlibPackages $ gomodulesDir project
pure $
DependencyResults
Expand All @@ -81,12 +83,17 @@ getDeps project goDynamicTactic = do
, dependencyManifestFiles = [gomodulesGomod project]
}
where
fallbacks :: (Has Diagnostics sig m, Has Exec sig m, Has ReadFS sig m) => m (Graphing Dependency, GraphBreadth)
fallbacks = goDotModAnalysis <||> Gomod.analyzeStatic (gomodulesGomod project)
-- `go list -json -deps all` marks std lib deps with a boolean, so Strategy.Go.GoListPackages does this filtering itself.
-- I think this can be removed when `go list -json -deps all` becomes the default.
filterGraph :: Maybe [GoStdlibDep] -> Graphing Dependency -> Graphing Dependency
filterGraph Nothing deps = deps
filterGraph (Just stdlib) deps = filterGoStdlibPackages stdlib deps

goDotModAnalysis :: (Has ReadFS sig m, Has Exec sig m, Has Diagnostics sig m) => m (Graphing Dependency, GraphBreadth)
goDotModAnalysis = context "Go.mod analysis" (Gomod.analyze' (gomodulesGomod project))

dynamicAnalysis :: (Has Exec sig m, Has Diagnostics sig m) => m (Graphing Dependency, GraphBreadth)
dynamicAnalysis =
context "Dynamic analysis" $ do
Expand All @@ -97,13 +104,13 @@ getDeps project goDynamicTactic = do

context "analysis using go list (V3 Resolver)" (GoListPackages.analyze (gomodulesDir project))

staticAnalysis :: (Has Exec sig m, Has ReadFS sig m, Has Diagnostics sig m) => GomodulesProject -> m (Graphing Dependency, GraphBreadth)
staticAnalysis project = context "Static analysis" (Gomod.analyze' (gomodulesGomod project))

staticAnalysisResults :: GomodulesProject -> (Graphing Dependency, GraphBreadth) -> DependencyResults
staticAnalysisResults proj (graph, graphBreadth) =
DependencyResults
{ dependencyGraph = graph
, dependencyGraphBreadth = graphBreadth
, dependencyManifestFiles = [gomodulesGomod proj]
}
staticAnalysis :: (Has Diagnostics sig m, Has ReadFS sig m) => GomodulesProject -> m DependencyResults
staticAnalysis proj = do
let projectPath = gomodulesGomod proj
(graph, breadth) <- context "Go.mod analysis (static)" $ Gomod.analyzeStatic projectPath
pure $
DependencyResults
{ dependencyGraph = graph
, dependencyGraphBreadth = breadth
, dependencyManifestFiles = [projectPath]
}

0 comments on commit 1847732

Please sign in to comment.