Skip to content

Commit

Permalink
Expose a static only Go modules tactic. (#1486)
Browse files Browse the repository at this point in the history
* Expose a static only Go modules tactic.

* really for real go static analysis

* Remove fallbacks. It already does that.

* active voice

* Document that transitive dependnecies aren't reported with Go mod static analysis.

* Fix typo.
  • Loading branch information
csasarak authored Dec 3, 2024
1 parent 4ed7eab commit 513a3a5
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 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: Expose a static only analysis method for Go. ([#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
4 changes: 4 additions & 0 deletions docs/references/strategies/languages/golang/gomodules.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ where:
- `replace` rewrites `require`s. In this example, our requires resolve to
`[github.com/example/one v1.2.3, github.com/example/other v2.0.0]`


This strategy will attempt to fill in transitive dependencies by calling out to Go tools.
If it fails or `fossa analyze` is invoked with `--static-analysis-only`, the strategy will report what it found in `go.mod` without any transitive dependencies.

## FAQ

### Why do I see a dependency in `go.mod`, but it is not reflected in FOSSA?
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
26 changes: 20 additions & 6 deletions src/Strategy/Gomodules.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ module Strategy.Gomodules (
getDeps,
mkProject,
GomodulesProject (..),
) where
)
where

import App.Fossa.Analyze.Types (AnalyzeProject (analyzeProjectStaticOnly), analyzeProject)
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 @@ -57,7 +58,7 @@ instance ToJSON GomodulesProject

instance AnalyzeProject GomodulesProject where
analyzeProject _ proj = asks useV3GoResolver >>= getDeps proj
analyzeProjectStaticOnly _ = const $ fatalText "Cannot analyze GoModule project statically"
analyzeProjectStaticOnly _ = staticAnalysis

mkProject :: GomodulesProject -> DiscoveredProject GomodulesProject
mkProject project =
Expand All @@ -71,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
(graph, graphBreadth) <-
context "Gomodules" $
dynamicAnalysis <||> guardStrictMode mode goDotModAnalysis
stdlib <- recover . context "Collect go standard library information" . listGoStdlibPackages $ gomodulesDir project
pure $
DependencyResults
Expand All @@ -86,8 +89,8 @@ getDeps project goDynamicTactic = do
filterGraph Nothing deps = deps
filterGraph (Just stdlib) deps = filterGoStdlibPackages stdlib deps

staticAnalysis :: (Has Exec sig m, Has ReadFS sig m, Has Diagnostics sig m) => m (Graphing Dependency, GraphBreadth)
staticAnalysis = context "Static analysis" (Gomod.analyze' (gomodulesGomod project))
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 =
Expand All @@ -98,3 +101,14 @@ getDeps project goDynamicTactic = do
\This option will be removed in a future release and result in an error."

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

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 513a3a5

Please sign in to comment.