From b5f97eb2cd7d6a591afaef67bde1c292bfccf044 Mon Sep 17 00:00:00 2001 From: Jessica Black Date: Tue, 5 Mar 2024 13:38:15 -0800 Subject: [PATCH] Reachability: custom jars (#1382) --- Changelog.md | 53 +++++++++-------- docs/features/vuln_reachability.md | 57 ++++++++++++++++--- .../references/files/fossa-yml.v3.schema.json | 19 ++++++- integration-test/Analysis/FixtureUtils.hs | 10 ++-- integration-test/Reachability/UploadSpec.hs | 38 +++++++++++-- src/App/Fossa/Analyze.hs | 2 +- src/App/Fossa/Config/Analyze.hs | 45 ++++++++++++++- src/App/Fossa/Config/ConfigFile.hs | 44 ++++++++++++++ src/App/Fossa/Init/.fossa.yml | 44 +++++++++----- src/App/Fossa/Reachability/Gradle.hs | 7 +-- src/App/Fossa/Reachability/Jar.hs | 20 ++++++- src/App/Fossa/Reachability/Maven.hs | 14 ++--- src/App/Fossa/Reachability/Types.hs | 15 ++++- src/App/Fossa/Reachability/Upload.hs | 29 ++++++++-- test/App/Fossa/Config/Utils.hs | 1 + .../Fossa/Configuration/ConfigurationSpec.hs | 1 + .../Configuration/TelemetryConfigSpec.hs | 1 + test/Reachability/UploadSpec.hs | 26 +++++---- .../maven-default-missing-jar/pom.xml | 16 ++++++ test/Test/Fixtures.hs | 1 + 20 files changed, 359 insertions(+), 84 deletions(-) create mode 100644 test/Reachability/testdata/maven-default-missing-jar/pom.xml diff --git a/Changelog.md b/Changelog.md index 73bdba55a8..97f592c99b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ # FOSSA CLI Changelog +## v3.9.8 +- Reachability: Users may now provide custom locations for the JAR files emitted by projects and used for reachability analysis ([#1382](https://github.com/fossas/fossa-cli/pull/1382)). + ## v3.9.7 - Add preflight permission checks to validate token type, subscription type, project permissions, and release group permissions [#1383](https://github.com/fossas/fossa-cli/pull/1383) @@ -7,45 +10,45 @@ - Add debug logs for build warnings in `analyze` commands [#1386](https://github.com/fossas/fossa-cli/pull/1386) ## v3.9.5 -- Maven: Fix hanging maven analysis [#1381](https://github.com/fossas/fossa-cli/pull/1381) +- Maven: Fix hanging maven analysis ([#1381](https://github.com/fossas/fossa-cli/pull/1381)). ## v3.9.4 -- Reachability: Includes reachability analysis in scan summary [#1379](https://github.com/fossas/fossa-cli/pull/1379) +- Reachability: Includes reachability analysis in scan summary ([#1379](https://github.com/fossas/fossa-cli/pull/1379)). ## v3.9.3 -- Update error structure [#1364](https://github.com/fossas/fossa-cli/pull/1364) +- Update error structure ([#1364](https://github.com/fossas/fossa-cli/pull/1364)). ## v3.9.2 -- Maven: Adds reachability analysis [#1372](https://github.com/fossas/fossa-cli/pull/1377) -- Gradle: Adds reachability analysis [#1377](https://github.com/fossas/fossa-cli/pull/1377) +- Maven: Adds reachability analysis ([#1372](https://github.com/fossas/fossa-cli/pull/1377)). +- Gradle: Adds reachability analysis ([#1377](https://github.com/fossas/fossa-cli/pull/1377)). ## v3.9.1 -- `--detect-dynamic`: Safely ignores scenarios in ldd output parsing where we run into not found error. ([#1376](https://github.com/fossas/fossa-cli/pull/1376)) +- `--detect-dynamic`: Safely ignores scenarios in ldd output parsing where we run into not found error ([#1376](https://github.com/fossas/fossa-cli/pull/1376)). ## v3.9.0 -- Emits a warning instead of an error when no analysis targets are found ([#1375](https://github.com/fossas/fossa-cli/pull/1375)) +- Emits a warning instead of an error when no analysis targets are found ([#1375](https://github.com/fossas/fossa-cli/pull/1375)). ## 3.8.37 -- Container Scans: Bugfix for some registry scans that fail with an STM error. ([#1370](https://github.com/fossas/fossa-cli/pull/1370)) +- Container Scans: Bugfix for some registry scans that fail with an STM error. ([#1370](https://github.com/fossas/fossa-cli/pull/1370)). ## v3.8.36 -- `fossa feedback`: Allow users to provide feedback on their cli experience ([#1368](https://github.com/fossas/fossa-cli/pull/1368)) -- Add preflight checks to validate API key, connection to FOSSA app, and ability to write to temp directory in relevant commands +- `fossa feedback`: Allow users to provide feedback on their cli experience ([#1368](https://github.com/fossas/fossa-cli/pull/1368)). +- Add preflight checks to validate API key, connection to FOSSA app, and ability to write to temp directory in relevant commands. ## v3.8.35 - Running `fossa analyze --detect-vendored` no longer fails if there are no detected vendored dependencies ([#1373](https://github.com/fossas/fossa-cli/pull/1373)). ## v3.8.34 -- Add color and update formatting in cli help commands ([#1367](https://github.com/fossas/fossa-cli/pull/1367)) +- Add color and update formatting in cli help commands ([#1367](https://github.com/fossas/fossa-cli/pull/1367)). ## v3.8.33 -- Removes warnings and tracebacks to stderr [#1358](https://github.com/fossas/fossa-cli/pull/1358) +- Removes warnings and tracebacks to stderr ([#1358](https://github.com/fossas/fossa-cli/pull/1358)). ## v3.8.32 -- Options: Add a `--static-only-analysis` option. ([#1362](https://github.com/fossas/fossa-cli/pull/1362)) +- Options: Add a `--static-only-analysis` option ([#1362](https://github.com/fossas/fossa-cli/pull/1362)). ## v3.8.31 @@ -55,33 +58,33 @@ ## v3.8.30 -- Fix an issue with long-option syntax for older versions of `sbt` ([#1356](https://github.com/fossas/fossa-cli/pull/1356)) -- Debug: add more logging for debugging missing dependencies. ([#1360](https://github.com/fossas/fossa-cli/pull/1360)) +- Fix an issue with long-option syntax for older versions of `sbt` ([#1356](https://github.com/fossas/fossa-cli/pull/1356)). +- Debug: add more logging for debugging missing dependencies ([#1360](https://github.com/fossas/fossa-cli/pull/1360)). ## v3.8.29 -- Prevents showing SCM warnings in fossa analyze, test, and report [#1354](https://github.com/fossas/fossa-cli/pull/1354) -- Pathfinder: Pathfinder has been deprecated and removed. ([#1350](https://github.com/fossas/fossa-cli/pull/1350)) +- Prevents showing SCM warnings in fossa analyze, test, and report ([#1354](https://github.com/fossas/fossa-cli/pull/1354)). +- Pathfinder: Pathfinder has been deprecated and removed ([#1350](https://github.com/fossas/fossa-cli/pull/1350)). ## v3.8.28 -- VSI: no longer reports paths inside of extracted archives with the `!_fossa.virtual_!` literal [#1345](https://github.com/fossas/fossa-cli/pull/1345) +- VSI: no longer reports paths inside of extracted archives with the `!_fossa.virtual_!` literal ([#1345](https://github.com/fossas/fossa-cli/pull/1345)). ## v3.8.27 -- Maven: Fix a bug that broke maven analysis if the build directory was in a non-standard location ([#1343](https://github.com/fossas/fossa-cli/pull/1343)) +- Maven: Fix a bug that broke maven analysis if the build directory was in a non-standard location ([#1343](https://github.com/fossas/fossa-cli/pull/1343)). ## v3.8.26 -- Maven: add support for maven submodule filtering [#1339](https://github.com/fossas/fossa-cli/pull/1339) +- Maven: add support for maven submodule filtering ([#1339](https://github.com/fossas/fossa-cli/pull/1339)). ## v3.8.25 -- Maven: add support for maven scope filtering ([#1331](https://github.com/fossas/fossa-cli/pull/1331)) -- `fossa init`: adds new `fossa init` command which creates `.fossa.yml.example`, and `fossa-deps.yml.example` file. ([#1323](https://github.com/fossas/fossa-cli/pull/1323)) +- Maven: add support for maven scope filtering ([#1331](https://github.com/fossas/fossa-cli/pull/1331)). +- `fossa init`: adds new `fossa init` command which creates `.fossa.yml.example`, and `fossa-deps.yml.example` file. ([#1323](https://github.com/fossas/fossa-cli/pull/1323)). ## v3.8.24 -- Python: use `pip` to determine transitive dependencies for setuptool projects that contain a req*.txt or setup.py file. ([#1334](https://github.com/fossas/fossa-cli/pull/1334)) -- Container Scanning: warn and exclude rpm packages that are missing attributes. ([#1335](https://github.com/fossas/fossa-cli/pull/1335)) +- Python: use `pip` to determine transitive dependencies for setuptool projects that contain a req*.txt or setup.py file. ([#1334](https://github.com/fossas/fossa-cli/pull/1334)). +- Container Scanning: warn and exclude rpm packages that are missing attributes ([#1335](https://github.com/fossas/fossa-cli/pull/1335)). ## v3.8.23 -- Custom License Scans: Support full-file uploads for custom license scans ([#1333](https://github.com/fossas/fossa-cli/pull/1333)) +- Custom License Scans: Support full-file uploads for custom license scans ([#1333](https://github.com/fossas/fossa-cli/pull/1333)). ## v3.8.22 - path: adds path dependency scanning functionality. ([#1327](https://github.com/fossas/fossa-cli/pull/1327)) diff --git a/docs/features/vuln_reachability.md b/docs/features/vuln_reachability.md index 58b472bef3..3ee7c7f351 100644 --- a/docs/features/vuln_reachability.md +++ b/docs/features/vuln_reachability.md @@ -2,16 +2,16 @@ ### What is Reachability? -Reachability Analysis is a security offering designed to enhance FOSSA's security analysis by providing context on vulnerable packages. It alleviates the constraints of traditional CVE assessments through the static analysis of application and dependency code, confirming the presence of vulnerable call paths. +Reachability Analysis is a security offering designed to enhance FOSSA's security analysis by providing context on vulnerable packages. It alleviates the constraints of traditional CVE assessments through the static analysis of application and dependency code, confirming the presence of vulnerable call paths. ### Limitations -- Reachability currently supports all Maven and Gradle projects dynamically analyzed by FOSSA CLI. +- Reachability currently supports all Maven and Gradle projects dynamically analyzed by FOSSA CLI. - The target jar of the project must exist, prior to the analysis. If the jar artifact is not present, or FOSSA CLI fails to associate this jar with project, FOSSA CLI will not perform reachability analysis. - Reachability requires that `java` is present in PATH, and `java` version must be greater than `1.8` (jdk8+). -For example, +For example, - if you are using maven, you should run `mvn package` to ensure jar artifact exists, prior to running `fossa analyze` - if you are using gradle, you should run `gradlew build` to ensure jar artifact exists, prior to running `fossa analyze` @@ -23,6 +23,50 @@ For Maven projects, FOSSA CLI performs an analysis to infer dependencies. If FOS For Gradle projects, FOSSA CLI invokes `./gradlew -I jsonpaths.gradle jsonPaths`. Where [jsonpaths.gradle](./../../scripts/jarpaths.gradle) is gradle script, which uses `java` plugin, and `jar` task associated with gradle to infer path of the built jar file. If neither of those are present, FOSSA CLI won't be able to identify jar artifacts for analysis. +### Custom JAR locations + +For both Gradle and Maven, it is possible to choose different locations for output JAR files, or to move them before FOSSA CLI runs. +For these cases, you can configure the locations directly. + +First, run `fossa list-targets` inside your project. For example: +```sh +❯ fossa list-targets +Found project: gradle@./ +Found target: gradle@./::app +``` + +This output specifies that a Gradle project was found at the root of the current directory (at `./`). +Note for which project(s) you wish to provide the paths to the built JAR files, we'll need these later. + +Now, create a `.fossa.yml` file if one does not already exist ([more information here](../references/files/fossa-yml.md)). +Add the following keys to it: +```yml +reachability: + jvmOutputs: +``` + +Inside the `jvmOutputs` object, create a map that describes, for each project path (noted down earlier) the paths to the JAR files that the project output. + +For example, let's say that the Gradle project at the root of my scan directory (at `./`) produces a JAR file named `app.jar`, and that I configured Gradle such that when it builds the project that `app.jar` file is also written to the root of my scan directory (so its path is `./app.jar`, relative to the scan root). +My config file would look like this: + +```yml +reachability: + jvmOutputs: + './': + - './app.jar' +``` + +You can also provide the absolute paths to make this more clear, if desired. For example, if my project was at `~/projects/example-project` and I wanted to use absolute paths in the config file, the file would look like this: +```yml +reachability: + jvmOutputs: + '/Users/me/projects/example-project': + - '/Users/me/projects/example-project/app.jar' +``` + +With this configuration, when FOSSA CLI analyzes a Maven or Gradle project at the specified path (in this case `/Users/me/projects/example-project`) instead of attempting to query the build tool for the location of the built JAR files it will instead use the values provided for that path (in this case `/Users/me/projects/example-project/app.jar`). + ### How do I debug reachability from `fossa-cli`? ```bash @@ -75,12 +119,12 @@ cat fossa.debug.json | jq '.bundleReachabilityEndpoint' } ``` -FOSSA CLI uses [jar-callgraph-1.0.0.jar](../../scripts/jar-callgraph-1.0.0.jar) to infer call path edges. +FOSSA CLI uses [jar-callgraph-1.0.0.jar](../../scripts/jar-callgraph-1.0.0.jar) to infer call path edges. FOSSA CLI uses `java -jar jar-callgraph-1.0.0.jar ./path/to/your/build.jar` command to record edges from the your target jar. If you are running into issues with reachability, please confirm that you can execute `java -jar jar-callgraph-1.0.0.jar ./path/to/your/build.jar` on your environment. - -## F.A.Q. +## F.A.Q. 1. What data from my codebase is uploaded to endpoint? @@ -174,4 +218,3 @@ Reachability analysis | skipped (partial graph) | Project has partial dependency graph (e.g. missing transitive dependencies) | | skipped (not supported) | Project is not supported for reachability analysis | | skipped (no dependency analysis) | Project's dependencies were not analyzed, so reachability cannot be computed | - diff --git a/docs/references/files/fossa-yml.v3.schema.json b/docs/references/files/fossa-yml.v3.schema.json index c6cd702ed6..fac6cb92a0 100644 --- a/docs/references/files/fossa-yml.v3.schema.json +++ b/docs/references/files/fossa-yml.v3.schema.json @@ -397,6 +397,23 @@ "$ref": "#/$defs/grepDefinition" } }, + "reachability": { + "type": "object", + "description": "Controls the Reachability computation functionality", + "properties": { + "jvmOutputs": { + "type": "object", + "description": "Manually specify the list of JAR files output by each discovered Maven or Gradle project", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "description": "The path to a JAR file output by the project" + } + } + } + } + }, "ignoreOrgWideCustomLicenseScanConfigs": { "type": "boolean", "default": false, @@ -477,4 +494,4 @@ "required": [ "version" ] -} \ No newline at end of file +} diff --git a/integration-test/Analysis/FixtureUtils.hs b/integration-test/Analysis/FixtureUtils.hs index d46207e1af..ee83834496 100644 --- a/integration-test/Analysis/FixtureUtils.hs +++ b/integration-test/Analysis/FixtureUtils.hs @@ -10,7 +10,7 @@ module Analysis.FixtureUtils ( TestC, performDiscoveryAndAnalyses, getArtifact, - testRunnerWithLogger, + testRunner, withResult, ) where @@ -122,8 +122,8 @@ type TestC m = $ StackC $ IgnoreTelemetryC m -testRunnerWithLogger :: TestC IO a -> FixtureEnvironment -> IO (Result a) -testRunnerWithLogger f env = +testRunner :: TestC IO a -> FixtureEnvironment -> IO (Result a) +testRunner f env = f & runExecIOWithinEnv env & runReadFSIO @@ -160,10 +160,10 @@ performDiscoveryAndAnalyses targetDir AnalysisTestFixture{..} = do _ <- sendIO $ runCmd environment buildCmd -- Perform discovery - discoveryResult <- sendIO $ testRunnerWithLogger (discover targetDir) environment + discoveryResult <- sendIO $ testRunner (discover targetDir) environment withResult discoveryResult $ \_ dps -> for dps $ \dp -> do - analysisResult <- sendIO $ testRunnerWithLogger (ignoreDebug $ analyzeProject (projectBuildTargets dp) (projectData dp)) environment + analysisResult <- sendIO $ testRunner (ignoreDebug $ analyzeProject (projectBuildTargets dp) (projectData dp)) environment withResult analysisResult $ \_ dr -> pure (dp, dr) where runCmd :: FixtureEnvironment -> Maybe (Command) -> IO () diff --git a/integration-test/Reachability/UploadSpec.hs b/integration-test/Reachability/UploadSpec.hs index 2dae490b77..e94a93f3d3 100644 --- a/integration-test/Reachability/UploadSpec.hs +++ b/integration-test/Reachability/UploadSpec.hs @@ -3,7 +3,7 @@ module Reachability.UploadSpec (spec) where -import Analysis.FixtureUtils (FixtureEnvironment (..), TestC, testRunnerWithLogger, withResult) +import Analysis.FixtureUtils (FixtureEnvironment (..), TestC, testRunner, withResult) import App.Fossa.Analyze.Project (ProjectResult (..)) import App.Fossa.Analyze.Types ( DiscoveredProjectIdentifier (..), @@ -15,6 +15,7 @@ import App.Fossa.Reachability.Types ( CallGraphAnalysis (..), ContentRef (..), ParsedJar (..), + ReachabilityConfig (..), SourceUnitReachability (..), ) import App.Fossa.Reachability.Upload ( @@ -22,8 +23,10 @@ import App.Fossa.Reachability.Upload ( callGraphOf, onlyFoundUnits, ) +import Control.Carrier.Reader (ReaderC, runReader) import Data.ByteString.Lazy qualified as LB import Data.Foldable (for_) +import Data.Map qualified as Map import Data.String.Conversion (toText) import Data.Text (Text) import Data.Text.Encoding qualified as TL @@ -42,6 +45,7 @@ import Path ( import Path.IO qualified as PIO import Test.Hspec (Spec, describe, it, runIO, shouldBe) import Text.RawString.QQ (r) +import Type.Operator (type ($)) import Types ( DiscoveredProjectType (MavenProjectType), GraphBreadth (..), @@ -60,7 +64,10 @@ java21 :: FixtureEnvironment java21 = NixEnv ["jdk21"] run :: FixtureEnvironment -> TestC IO a -> IO (Result a) -run env act = testRunnerWithLogger act env +run env act = testRunner act env + +runConf :: FixtureEnvironment -> ReachabilityConfig -> (ReaderC ReachabilityConfig $ TestC IO) a -> IO (Result a) +runConf env conf act = testRunner (runReader conf act) env spec :: Spec spec = describe "Reachability" $ do @@ -86,7 +93,7 @@ spec = describe "Reachability" $ do it "should retrieve call graph" $ do let (dpi, dps) = mavenCompleteScan projDir let expected = SourceUnitReachabilityFound dpi (Success [] (mavenCompleteScanUnit projDir jarFile)) - resp <- run java8 $ callGraphOf dps + resp <- runConf java8 mempty $ callGraphOf dps withResult resp $ \_ res -> res `shouldBe` expected describe "analyzeForReachability" $ do @@ -96,12 +103,32 @@ spec = describe "Reachability" $ do it "should return analyzed reachability unit" $ do let (_, dps) = mavenCompleteScan projDir let expected = [mavenCompleteScanUnit projDir jarFile] - analyzed <- run java8 $ analyzeForReachability [dps] + analyzed <- runConf java8 mempty $ analyzeForReachability [dps] + withResult analyzed $ \_ analyzed' -> (onlyFoundUnits analyzed') `shouldBe` expected + + describe "user provided jar" $ do + projDir <- ( sampleMavenProjectMissingOutputJarDir) <$> runIO PIO.getCurrentDir + jarFile <- ( sampleMavenProjectJar) <$> runIO PIO.getCurrentDir + + it "should fail to analyze reachability when jar is missing" $ do + let (_, dps) = mavenCompleteScan projDir + let expected = [mavenEmptyScanUnit projDir] + analyzed <- runConf java8 mempty $ analyzeForReachability [dps] + withResult analyzed $ \_ analyzed' -> (onlyFoundUnits analyzed') `shouldBe` expected + + it "should succeed analyzing the project when a missing jar is manually specified" $ do + let conf = ReachabilityConfig $ Map.fromList [(projDir, [jarFile])] + let (_, dps) = mavenCompleteScan projDir + let expected = [mavenCompleteScanUnit projDir jarFile] + analyzed <- runConf java8 conf $ analyzeForReachability [dps] withResult analyzed $ \_ analyzed' -> (onlyFoundUnits analyzed') `shouldBe` expected sampleMavenProjectDir :: Path Rel Dir sampleMavenProjectDir = $(mkRelDir "test/Reachability/testdata/maven-default/") +sampleMavenProjectMissingOutputJarDir :: Path Rel Dir +sampleMavenProjectMissingOutputJarDir = $(mkRelDir "test/Reachability/testdata/maven-default-missing-jar/") + sampleMavenProjectJar :: Path Rel File sampleMavenProjectJar = $(mkRelFile "test/Reachability/testdata/maven-default/target/project-1.0.0.jar") @@ -117,6 +144,9 @@ mavenCompleteScanUnit projDir jarFile = (ContentRaw sampleJarParsedContent') ] +mavenEmptyScanUnit :: Path Abs Dir -> SourceUnitReachability +mavenEmptyScanUnit projDir = mkReachabilityUnit projDir [] + mkDiscoveredProjectScan :: DiscoveredProjectType -> Path Abs Dir -> GraphBreadth -> (DiscoveredProjectIdentifier, DiscoveredProjectScan) mkDiscoveredProjectScan projectType dir breadth = ( dpi diff --git a/src/App/Fossa/Analyze.hs b/src/App/Fossa/Analyze.hs index 2614cc00c2..3475f45525 100644 --- a/src/App/Fossa/Analyze.hs +++ b/src/App/Fossa/Analyze.hs @@ -400,7 +400,7 @@ analyze cfg = Diag.context "fossa-analyze" $ do (False, _) -> traverse (withPathDependencyNudge includeAll) filteredProjects logDebug $ "Filtered projects with path dependencies: " <> pretty (show filteredProjects') - reachabilityUnitsResult <- analyzeForReachability projectScans + reachabilityUnitsResult <- Diag.context "reachability analysis" . runReader (Config.reachabilityConfig cfg) $ analyzeForReachability projectScans let reachabilityUnits = onlyFoundUnits reachabilityUnitsResult let analysisResult = AnalysisScanResult projectScans vsiResults binarySearchResults manualSrcUnits dynamicLinkedResults maybeLernieResults reachabilityUnitsResult diff --git a/src/App/Fossa/Config/Analyze.hs b/src/App/Fossa/Config/Analyze.hs index bf63df26c3..3c17345105 100644 --- a/src/App/Fossa/Config/Analyze.hs +++ b/src/App/Fossa/Config/Analyze.hs @@ -45,6 +45,7 @@ import App.Fossa.Config.Common ( targetOpt, validateDir, validateExists, + validateFile, ) import App.Fossa.Config.ConfigFile ( ConfigFile (..), @@ -53,12 +54,14 @@ import App.Fossa.Config.ConfigFile ( ExperimentalConfigs (..), ExperimentalGradleConfigs (..), OrgWideCustomLicenseConfigPolicy (..), + ReachabilityConfigFile (..), VendoredDependencyConfigs (..), mergeFileCmdMetadata, resolveConfigFile, ) import App.Fossa.Config.EnvironmentVars (EnvVars (..)) import App.Fossa.Lernie.Types (GrepEntry (..), GrepOptions (..)) +import App.Fossa.Reachability.Types (ReachabilityConfig (..)) import App.Fossa.Subcommand (EffStack, GetCommonOpts (getCommonOpts), GetSeverity (getSeverity), SubCommand (SubCommand)) import App.Fossa.VSI.Types qualified as VSI import App.Types ( @@ -72,12 +75,16 @@ import App.Types ( import Control.Effect.Diagnostics ( Diagnostics, Has, + context, fatalText, + recover, ) import Control.Effect.Lift (Lift) import Control.Monad (when) import Data.Aeson (ToJSON (toEncoding), defaultOptions, genericToEncoding) import Data.Flag (Flag, flagOpt, fromFlag) +import Data.Map qualified as Map +import Data.Maybe (catMaybes) import Data.Monoid.Extra (isMempty) import Data.Set (Set) import Data.Set qualified as Set @@ -110,7 +117,7 @@ import Options.Applicative ( switch, (<|>), ) -import Path (Abs, Dir, Path, Rel) +import Path (Abs, Dir, File, Path, Rel) import Path.Extra (SomePath) import Prettyprinter (Doc, annotate, indent) import Prettyprinter.Render.Terminal (AnsiStyle, Color (Green, Red), color) @@ -246,6 +253,7 @@ data AnalyzeConfig = AnalyzeConfig , grepOptions :: GrepOptions , customFossaDepsFile :: Maybe FilePath , allowedTacticTypes :: AnalysisTacticTypes + , reachabilityConfig :: ReachabilityConfig } deriving (Eq, Ord, Show, Generic) @@ -481,6 +489,7 @@ mergeStandardOpts maybeConfig envvars cliOpts@AnalyzeCliOpts{..} = do if fromFlag StaticOnlyTactics analyzeStaticOnlyTactics then StaticOnly else Any + reachabilityConfig = collectReachabilityOptions maybeConfig firstPartyScansFlag <- case (fromFlag ForceFirstPartyScans analyzeForceFirstPartyScans, fromFlag ForceNoFirstPartyScans analyzeForceNoFirstPartyScans) of @@ -508,6 +517,7 @@ mergeStandardOpts maybeConfig envvars cliOpts@AnalyzeCliOpts{..} = do <*> pure grepOptions <*> pure customFossaDepsFile <*> pure allowedTacticType + <*> resolveReachabilityOptions reachabilityConfig collectMavenScopeFilters :: ( Has Diagnostics sig m @@ -627,3 +637,36 @@ collectModeOptions AnalyzeCliOpts{..} = do , dynamicLinkingTarget = DynamicLinkInspect resolvedDynamicLinkTarget , binaryDiscoveryEnabled = analyzeBinaryDiscoveryMode } + +collectReachabilityOptions :: Maybe ConfigFile -> ReachabilityConfigFile +collectReachabilityOptions (Just ConfigFile{configReachability = (Just conf)}) = conf +collectReachabilityOptions _ = mempty + +resolveReachabilityOptions :: + ( Has Diagnostics sig m + , Has (Lift IO) sig m + , Has ReadFS sig m + ) => + ReachabilityConfigFile -> + m ReachabilityConfig +resolveReachabilityOptions cf = + ReachabilityConfig + . Map.fromList + . catMaybes + <$> traverse resolveProjectAndJars outputs + where + outputs :: [(String, [String])] + outputs = Map.toList $ configFileReachabilityJvmOutputs cf + + resolveProjectAndJars :: (Has Diagnostics sig m, Has (Lift IO) sig m, Has ReadFS sig m) => (String, [String]) -> m (Maybe (Path Abs Dir, [Path Abs File])) + resolveProjectAndJars (projectPath, jarPaths) = + context ("resolve provided jars for package at '" <> toText projectPath <> "'") $ + resolveProject projectPath >>= \case + Just projectPath' -> Just . (projectPath',) . catMaybes <$> traverse resolveJar jarPaths + Nothing -> pure Nothing + + resolveJar :: (Has Diagnostics sig m, Has (Lift IO) sig m, Has ReadFS sig m) => String -> m (Maybe (Path Abs File)) + resolveJar jarPath = context ("resolve provided jar path '" <> toText jarPath <> "'") . recover $ validateFile jarPath + + resolveProject :: (Has Diagnostics sig m, Has (Lift IO) sig m, Has ReadFS sig m) => String -> m (Maybe (Path Abs Dir)) + resolveProject projectPath = context "resolve provided project path" . recover $ validateDir projectPath diff --git a/src/App/Fossa/Config/ConfigFile.hs b/src/App/Fossa/Config/ConfigFile.hs index 9a7784a055..f2049699f9 100644 --- a/src/App/Fossa/Config/ConfigFile.hs +++ b/src/App/Fossa/Config/ConfigFile.hs @@ -17,6 +17,7 @@ module App.Fossa.Config.ConfigFile ( ExperimentalGradleConfigs (..), VendoredDependencyConfigs (..), MavenScopeConfig (..), + ReachabilityConfigFile (..), mergeFileCmdMetadata, resolveLocalConfigFile, ) where @@ -37,6 +38,7 @@ import Control.Effect.Diagnostics ( import Control.Effect.Lift (Lift) import Data.Aeson ( FromJSON (parseJSON), + ToJSON, withObject, withText, (.!=), @@ -46,6 +48,8 @@ import Data.Aeson ( import Data.Error (createBody, renderErrataStack) import Data.Foldable (asum) import Data.Functor (($>)) +import Data.Map (Map) +import Data.Map qualified as Map import Data.Set (Set) import Data.Set qualified as Set import Data.String.Conversion (ToString (toString), ToText (toText)) @@ -196,6 +200,7 @@ data ConfigFile = ConfigFile , configTelemetry :: Maybe ConfigTelemetry , configCustomLicenseSearch :: Maybe [ConfigGrepEntry] , configKeywordSearch :: Maybe [ConfigGrepEntry] + , configReachability :: Maybe ReachabilityConfigFile , configOrgWideCustomLicenseConfigPolicy :: OrgWideCustomLicenseConfigPolicy , configConfigFilePath :: Path Abs File } @@ -276,6 +281,7 @@ instance FromJSON (Path Abs File -> ConfigFile) where <*> obj .:? "telemetry" <*> obj .:? "customLicenseSearch" <*> obj .:? "experimentalKeywordSearch" + <*> obj .:? "reachability" <*> parseIgnoreOrgWideCustomLicenseScanConfigs obj where parseIgnoreOrgWideCustomLicenseScanConfigs obj = do @@ -365,3 +371,41 @@ instance FromJSON VendoredDependencyConfigs where <$> (obj .:? "forceRescans" .!= False) <*> (obj .:? "scanMethod") <*> (obj .:? "licenseScanPathFilters") + +-- | Configuration for reachability analysis. +newtype ReachabilityConfigFile = ReachabilityConfigFile + { configFileReachabilityJvmOutputs :: Map String [String] + -- ^ Reachability on JVM projects relies on analyzing the binary JAR files + -- emitted by the build process. FOSSA CLI tries to identify these from project metadata, + -- but this may not work for all projects. + -- + -- The intention of this config is to allow users to specify their own locations + -- in these situations. + -- + -- The key is the project path (found via @fossa list-targets@) + -- and the value is the list of JAR files built by that project. + -- + -- Example: + -- + -- > ; fossa list-targets + -- > Found project: maven@./ + -- > Found target: maven@./:com.example.app:example-artifact + -- + -- The config map should look like this when associating JARs to the project at @./@: + -- + -- > { + -- > "./": [ + -- > "some/other/example-artifact.jar", + -- > "yet/another/example-artifact.jar", + -- > ] + -- > } + -- + -- In particular, note that the @target@ entry is ignored. + -- This config is only concerned with targets. + } + deriving (Eq, Ord, Show, Monoid, Semigroup, ToJSON) + +instance FromJSON ReachabilityConfigFile where + parseJSON = withObject "ReachabilityConfig" $ \obj -> + ReachabilityConfigFile + <$> (obj .:? "jvmOutputs" .!= Map.empty) diff --git a/src/App/Fossa/Init/.fossa.yml b/src/App/Fossa/Init/.fossa.yml index 9f3967c9b6..2098a5d64f 100644 --- a/src/App/Fossa/Init/.fossa.yml +++ b/src/App/Fossa/Init/.fossa.yml @@ -31,10 +31,10 @@ version: 3 # # - Git: From .git/config file or project's remote "origin" URL. # # - SVN: From "Repository Root" obtained using 'svn info'. # # - No VCS: Name of the project's directory. -# # +# # # # NOTE: -# # A project's ID cannot be modified after a project is created. If you change the ID, -# # you will be interacting with a different project. If the new ID does not exist, +# # A project's ID cannot be modified after a project is created. If you change the ID, +# # you will be interacting with a different project. If the new ID does not exist, # # a new project will be created for it. # id: github.com/fossas/fossa-cli # @@ -115,12 +115,12 @@ version: 3 # vendoredDependencies: # # If true, forces a re-scan of all vendored dependencies on every run. Default: false # forceRescans: false -# +# # # Determines whether vendored dependencies are scanned using "Archive Upload" or "CLI License Scan" method. # # Possible values: ArchiveUpload, CLILicenseScan. # # The default is usually "CLILicenseScan", but your organization may have opted to default to "ArchiveUpload". # scanMethod: CLILicenseScan -# +# # # License Scan Path Filters Configuration # # Path filtering to omit some files or directories from license scanning. # # See documentation for details: https://github.com/fossas/fossa-cli/blob/master/docs/features/vendored-dependencies.md#path-filtering @@ -137,15 +137,15 @@ version: 3 # # See documentation for details: https://github.com/fossas/fossa-cli/blob/master/docs/references/files/fossa-yml.md#analysis-target-configuration # # See walkthough for example usage: https://github.com/fossas/fossa-cli/blob/master/docs/walkthroughs/analysis-target-configuration.md # targets: -# # The list of only targets that should be scanned. When used alongside paths.only, +# # The list of only targets that should be scanned. When used alongside paths.only, # # the intersection of the two lists is taken to find targets for scanning. # only: # - type: maven # path: foo/bar # -# # The list of exclude targets which should be excluded from scanning. -# # The targets listed in the exclude section will override the targets listed in the only -# # sections. This feature is used most effectively to remove specific targets from a directory. +# # The list of exclude targets which should be excluded from scanning. +# # The targets listed in the exclude section will override the targets listed in the only +# # sections. This feature is used most effectively to remove specific targets from a directory. # exclude: # - type: bundler # path: prod/docker @@ -157,14 +157,14 @@ version: 3 # # See walkthough for example usage: https://github.com/fossas/fossa-cli/blob/master/docs/walkthroughs/analysis-target-configuration.md # paths: # # The list of paths to only allow scanning within. -# # This section is most commonly used when you would like to restrict scanning +# # This section is most commonly used when you would like to restrict scanning # # to a certain list of directories from the root of your project. # only: # - ./production # # # The list of paths to exclude from scanning in your directory. -# # This section is intended to be used as the inverse to paths.only. -# # If you have a certain directory such as development you wish to exclude, +# # This section is intended to be used as the inverse to paths.only. +# # If you have a certain directory such as development you wish to exclude, # # paths.exclude enables you to do this. # exclude: # - ./vendor/django/test @@ -177,7 +177,7 @@ version: 3 # scope: full # # -# # FOSSA offers the ability to search your codebase using regular expressions +# # FOSSA offers the ability to search your codebase using regular expressions # # and to report matches. 'customLicenseSearch' can be used to search codebase # # with regular expression to mark custom licenses, with project. # # See documentation for details: https://github.com/fossas/fossa-cli/blob/master/docs/features/custom-license-and-keyword-searches.md#custom-license-and-keyword-searches @@ -195,3 +195,21 @@ version: 3 # experimentalKeywordSearch: # - matchCriteria: abc123 # name: Password +# +# # Configures the Reachability feature, if it is used. +# # See documentation for details: https://github.com/fossas/fossa-cli/blob/master/docs/features/vuln_reachability.md +# reachability: +# # If needed, add a map here in the shape of: +# # ``` +# # '{project path}': +# # - '{output jar path}' +# # - '{output jar path}' +# # ``` +# # This allows you to, for projects analyzed for reachability information (indicated by the key), +# # specify the paths to the JAR files built by that project (indicated by the value). +# # +# # These JAR files are needed to enable analyzing the built result for vulnerability information. +# jvmOutputs: +# '{project path}': +# - '{output jar path}' +# - '{output jar path}' diff --git a/src/App/Fossa/Reachability/Gradle.hs b/src/App/Fossa/Reachability/Gradle.hs index 556108f4f5..8e9d1fb491 100644 --- a/src/App/Fossa/Reachability/Gradle.hs +++ b/src/App/Fossa/Reachability/Gradle.hs @@ -2,7 +2,7 @@ module App.Fossa.Reachability.Gradle (gradleJarCallGraph, jarFileSignifier, jarPathsFromScriptOutput) where -import App.Fossa.Reachability.Jar (callGraphFromJar, isValidJar) +import App.Fossa.Reachability.Jar (callGraphFromJars, isValidJar) import App.Fossa.Reachability.Types (CallGraphAnalysis (..)) import App.Support (reportDefectWithDebugBundle) import Control.Carrier.Lift (Lift) @@ -15,7 +15,7 @@ import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as BL import Data.Error (createErrataWithHeaderOnly) import Data.FileEmbed.Extra (embedFile') -import Data.Maybe (catMaybes, fromMaybe) +import Data.Maybe (fromMaybe) import Data.Set (Set, toList) import Data.Set.NonEmpty (toSet) import Data.String.Conversion (ToString (..), ToText (toText), decodeUtf8) @@ -43,8 +43,7 @@ gradleJarCallGraph :: m CallGraphAnalysis gradleJarCallGraph dir = do jars <- runReader (mempty :: AllFilters) $ getJarsByBuild dir - parsedJars <- traverse callGraphFromJar jars - pure $ JarAnalysis (catMaybes parsedJars) + callGraphFromJars jars getJarsByBuild :: ( Has (Lift IO) sig m diff --git a/src/App/Fossa/Reachability/Jar.hs b/src/App/Fossa/Reachability/Jar.hs index 723171cd39..d5204c9913 100644 --- a/src/App/Fossa/Reachability/Jar.hs +++ b/src/App/Fossa/Reachability/Jar.hs @@ -2,13 +2,15 @@ module App.Fossa.Reachability.Jar ( callGraphFromJar, + callGraphFromJars, isValidJar, ) where -import App.Fossa.Reachability.Types (ContentRef (ContentRaw), ParsedJar (..)) +import App.Fossa.Reachability.Types (CallGraphAnalysis (..), ContentRef (ContentRaw), ParsedJar (..)) import Control.Effect.Diagnostics ( Diagnostics, ToDiagnostic, + context, recover, renderDiagnostic, warnOnErr, @@ -18,9 +20,11 @@ import Control.Effect.Lift (sendIO) import Data.ByteString qualified as BS import Data.Error (createErrataWithHeaderOnly) import Data.FileEmbed.Extra (embedFile') +import Data.Maybe (catMaybes) import Data.String.Conversion (toText) import Data.Text (isSuffixOf) import Effect.Exec (AllowErr (Never), Command (..), Exec, Has, execThrow) +import Effect.Logger (Logger) import Effect.ReadFS (ReadFS, doesFileExist) import Errata (Errata) import Path (Abs, File, Path, fileExtension, fromAbsDir, parent, toFilePath) @@ -74,6 +78,20 @@ instance ToDiagnostic FailedToParseJar where renderDiagnostic (FailedToParseJar jar) = createErrataWithHeaderOnly $ "Could not read from jar, so skipping: " <> toText (show jar) +-- | Like @mavenJarCallGraph@, but used when the list of JARs to parse is already available +-- and works for any Java project. +callGraphFromJars :: + ( Has Logger sig m + , Has Diagnostics sig m + , Has Exec sig m + , Has (Lift IO) sig m + ) => + [Path Abs File] -> + m CallGraphAnalysis +callGraphFromJars jars = context ("build call graph from " <> toText (show jars)) $ do + parsedJars <- traverse callGraphFromJar jars + pure $ JarAnalysis (catMaybes parsedJars) + -- True if jar exist, and is not likely test jar, otherwise False isValidJar :: (Has ReadFS sig m) => Path Abs File -> m Bool isValidJar file = do diff --git a/src/App/Fossa/Reachability/Maven.hs b/src/App/Fossa/Reachability/Maven.hs index b6feca2ee8..da30cc8637 100644 --- a/src/App/Fossa/Reachability/Maven.hs +++ b/src/App/Fossa/Reachability/Maven.hs @@ -3,10 +3,10 @@ module App.Fossa.Reachability.Maven ( getJarsByBuild, ) where -import App.Fossa.Reachability.Jar (callGraphFromJar, isValidJar) +import App.Fossa.Reachability.Jar (callGraphFromJars, isValidJar) import App.Fossa.Reachability.Types (CallGraphAnalysis (..)) import Control.Carrier.Lift (Lift) -import Control.Effect.Diagnostics (Diagnostics, fromEither, recover) +import Control.Effect.Diagnostics (Diagnostics, context, fromEither, recover) import Control.Monad (join) import Control.Monad.List (filterM) import Data.Map qualified as Map @@ -25,6 +25,8 @@ import Strategy.Maven.Pom.PomFile ( ) import Text.Pretty.Simple (pShow) +-- | Discovers the JAR files associated with the project at the provided path, +-- then returns the parsed results of analyzing these JARs. mavenJarCallGraph :: ( Has Logger sig m , Has ReadFS sig m @@ -34,12 +36,10 @@ mavenJarCallGraph :: ) => Path Abs Dir -> m CallGraphAnalysis -mavenJarCallGraph dir = do +mavenJarCallGraph dir = context ("build call graph for " <> toText dir) $ do jars <- getJarsByBuild dir logDebug . pretty $ "found jars: " ++ show jars - - parsedJars <- traverse callGraphFromJar jars - pure $ JarAnalysis (catMaybes parsedJars) + callGraphFromJars jars getJarsByBuild :: ( Has Logger sig m @@ -48,7 +48,7 @@ getJarsByBuild :: ) => Path Abs Dir -> m [Path Abs File] -getJarsByBuild dir = do +getJarsByBuild dir = context ("find jars from build for project at '" <> toText dir <> "'") $ do mvnProjectClosures <- findProjects dir let pomPathsAndPom = concatMap (Map.elems . closurePoms) mvnProjectClosures diff --git a/src/App/Fossa/Reachability/Types.hs b/src/App/Fossa/Reachability/Types.hs index f3bbf14439..b9f5a31957 100644 --- a/src/App/Fossa/Reachability/Types.hs +++ b/src/App/Fossa/Reachability/Types.hs @@ -3,18 +3,31 @@ module App.Fossa.Reachability.Types ( SourceUnitReachability (..), ParsedJar (..), ContentRef (..), + ReachabilityConfig (..), reachabilityRawJson, reachabilityEndpointJson, ) where import Data.Aeson (ToJSON (..), Value, object, (.=)) import Data.ByteString.Lazy (ByteString) +import Data.Map (Map) import Data.String.Conversion (ConvertUtf8 (decodeUtf8)) import Data.Text (Text) import GHC.Generics (Generic) -import Path (Abs, File, Path) +import Path (Abs, Dir, File, Path) import Srclib.Types (Locator, OriginPath) +-- | Reachability on JVM projects relies on analyzing the binary JAR files +-- emitted by the build process. FOSSA CLI tries to identify these from project metadata, +-- but this may not work for all projects. +-- +-- This config allows users to specify custom locations for the JAR file(s) +-- built from projects in their scan. +newtype ReachabilityConfig = ReachabilityConfig + { configReachabilityJvmOutputs :: Map (Path Abs Dir) [Path Abs File] + } + deriving (Eq, Ord, Show, Monoid, Semigroup, ToJSON) + data SourceUnitReachability = SourceUnitReachability { srcUnitType :: Text , srcUnitManifest :: Text diff --git a/src/App/Fossa/Reachability/Upload.hs b/src/App/Fossa/Reachability/Upload.hs index 69aa91ad70..d6d7caff16 100644 --- a/src/App/Fossa/Reachability/Upload.hs +++ b/src/App/Fossa/Reachability/Upload.hs @@ -14,11 +14,13 @@ import App.Fossa.Analyze.Types ( dpiProjectType, ) import App.Fossa.Reachability.Gradle (gradleJarCallGraph) +import App.Fossa.Reachability.Jar (callGraphFromJars) import App.Fossa.Reachability.Maven (mavenJarCallGraph) import App.Fossa.Reachability.Types ( CallGraphAnalysis (..), ContentRef (..), ParsedJar (..), + ReachabilityConfig (..), SourceUnitReachability (..), reachabilityEndpointJson, reachabilityRawJson, @@ -30,7 +32,9 @@ import Control.Effect.Debug (Debug, debugMetadata) import Control.Effect.Diagnostics (Diagnostics, context) import Control.Effect.FossaApiClient (FossaApiClient, uploadBuildForReachability, uploadContentForReachability) import Control.Effect.Lift (Lift) +import Control.Effect.Reader (Reader, ask) import Data.List (nub) +import Data.Map qualified as Map import Data.Maybe (mapMaybe) import Diag.Result (Result (..)) import Effect.Exec (Exec) @@ -52,6 +56,7 @@ analyzeForReachability :: , Has Exec sig m , Has (Lift IO) sig m , Has Debug sig m + , Has (Reader ReachabilityConfig) sig m ) => [DiscoveredProjectScan] -> m [SourceUnitReachabilityAttempt] @@ -103,10 +108,12 @@ callGraphOf :: , Has Exec sig m , Has (Lift IO) sig m , Has Debug sig m + , Has (Reader ReachabilityConfig) sig m ) => DiscoveredProjectScan -> m SourceUnitReachabilityAttempt callGraphOf (Scanned dpi (Success _ projectResult)) = do + let projectPath = projectResultPath projectResult let srcUnit = projectToSourceUnit False projectResult let dependencies = dependenciesOf srcUnit let displayId = sourceUnitType srcUnit <> "@" <> sourceUnitManifest srcUnit @@ -119,6 +126,8 @@ callGraphOf (Scanned dpi (Success _ projectResult)) = do , srcUnitDependencies = dependencies , callGraphAnalysis = NoCallGraphAnalysis } + (reachabilityConfig :: ReachabilityConfig) <- ask + let reachabilityJarsByProject = configReachabilityJvmOutputs reachabilityConfig case (projectResultGraphBreadth projectResult, dpiProjectType dpi) of -- if we do not have complete graph, i.e.. missing transitive dependencies -- it is impossible to perform reachability, as we may not have all symbols @@ -127,14 +136,26 @@ callGraphOf (Scanned dpi (Success _ projectResult)) = do logInfo . pretty $ "FOSSA CLI does not support reachability analysis, with partial dependencies graph (skipping: " <> displayId <> ")" pure . SourceUnitReachabilitySkippedPartialGraph $ dpi (Complete, MavenProjectType) -> context "maven" $ do - logDebug . pretty $ "Trying to infer build jars from maven project: " <> show (projectResultPath projectResult) - analysis <- Diag.errorBoundaryIO . diagToDebug $ mavenJarCallGraph (projectResultPath projectResult) + analysis <- Diag.errorBoundaryIO . diagToDebug $ case Map.lookup projectPath reachabilityJarsByProject of + Just jars -> do + logDebug . pretty $ "Using user-specified jars for maven project: " <> show projectPath + logDebug . pretty $ " " <> show jars + callGraphFromJars jars + Nothing -> do + logDebug . pretty $ "Trying to infer build jars from maven project: " <> show (projectResultPath projectResult) + mavenJarCallGraph (projectResultPath projectResult) case analysis of Success wg r -> pure $ SourceUnitReachabilityFound dpi (Success wg $ unit{callGraphAnalysis = r}) Failure wg eg -> pure $ SourceUnitReachabilityFound dpi (Failure wg eg) (Complete, GradleProjectType) -> context "gradle" $ do - logDebug . pretty $ "Trying to infer build jars from gradle project: " <> show (projectResultPath projectResult) - analysis <- Diag.errorBoundaryIO . diagToDebug $ gradleJarCallGraph (projectResultPath projectResult) + analysis <- Diag.errorBoundaryIO . diagToDebug $ case Map.lookup projectPath reachabilityJarsByProject of + Just jars -> do + logDebug . pretty $ "Using user-specified jars for gradle project: " <> show projectPath + logDebug . pretty $ " " <> show jars + callGraphFromJars jars + Nothing -> do + logDebug . pretty $ "Trying to infer build jars from gradle project: " <> show (projectResultPath projectResult) + gradleJarCallGraph (projectResultPath projectResult) case analysis of Success wg r -> pure $ SourceUnitReachabilityFound dpi (Success wg $ unit{callGraphAnalysis = r}) Failure wg eg -> pure $ SourceUnitReachabilityFound dpi (Failure wg eg) diff --git a/test/App/Fossa/Config/Utils.hs b/test/App/Fossa/Config/Utils.hs index 941216ebf7..c5abb8f761 100644 --- a/test/App/Fossa/Config/Utils.hs +++ b/test/App/Fossa/Config/Utils.hs @@ -43,6 +43,7 @@ configFile path = , configOrgWideCustomLicenseConfigPolicy = Use , configConfigFilePath = path , configMavenScope = Nothing + , configReachability = Nothing } fixtureDir :: Path Rel Dir diff --git a/test/App/Fossa/Configuration/ConfigurationSpec.hs b/test/App/Fossa/Configuration/ConfigurationSpec.hs index 557ae78208..055a404bf3 100644 --- a/test/App/Fossa/Configuration/ConfigurationSpec.hs +++ b/test/App/Fossa/Configuration/ConfigurationSpec.hs @@ -40,6 +40,7 @@ expectedConfigFile path = , configKeywordSearch = Just expectedKeywordSearch , configOrgWideCustomLicenseConfigPolicy = Use , configConfigFilePath = path + , configReachability = Nothing } expectedConfigProject :: ConfigProject diff --git a/test/App/Fossa/Configuration/TelemetryConfigSpec.hs b/test/App/Fossa/Configuration/TelemetryConfigSpec.hs index 4e96018d5d..11ce30efc5 100644 --- a/test/App/Fossa/Configuration/TelemetryConfigSpec.hs +++ b/test/App/Fossa/Configuration/TelemetryConfigSpec.hs @@ -81,6 +81,7 @@ defaultConfigFile = , configKeywordSearch = Nothing , configOrgWideCustomLicenseConfigPolicy = Use , configConfigFilePath = configPath + , configReachability = Nothing } mockApiKeyRaw :: Text diff --git a/test/Reachability/UploadSpec.hs b/test/Reachability/UploadSpec.hs index c0753a95d3..513b456f42 100644 --- a/test/Reachability/UploadSpec.hs +++ b/test/Reachability/UploadSpec.hs @@ -13,6 +13,7 @@ import App.Fossa.Reachability.Types ( CallGraphAnalysis (JarAnalysis), ContentRef (ContentRaw, ContentStoreKey), ParsedJar (..), + ReachabilityConfig, SourceUnitReachability ( callGraphAnalysis, srcUnitManifest, @@ -28,6 +29,7 @@ import App.Fossa.Reachability.Upload ( upload, ) import Control.Algebra (Has) +import Control.Carrier.Reader (ReaderC, runReader) import Control.Effect.FossaApiClient ( FossaApiClientF (..), ) @@ -78,30 +80,33 @@ dependenciesOfSpec = describe "dependenciesOf" $ ] expected `shouldBe` seen +run :: ReaderC ReachabilityConfig m a -> m a +run = runReader (mempty :: ReachabilityConfig) + callGraphOfSpec :: Spec callGraphOfSpec = describe "callGraphOf" $ do it' "should return SkippedMissingDependencyAnalysis if project was skipped" $ do dir <- ( sampleMavenProjectDir) <$> PIO.getCurrentDir let (dps, dpi) = skippedProject dir - res <- callGraphOf dps + res <- run $ callGraphOf dps res `shouldBe'` (SourceUnitReachabilitySkippedMissingDependencyAnalysis dpi) it' "should return SkippedMissingDependencyAnalysis if project was skipped due to default filtering" $ do dir <- ( sampleMavenProjectDir) <$> PIO.getCurrentDir let (dps, dpi) = skippedProjectByDefaultFilter dir - res <- callGraphOf dps + res <- run $ callGraphOf dps res `shouldBe'` (SourceUnitReachabilitySkippedMissingDependencyAnalysis dpi) it' "should return SkippedPartialGraph if graph depth is partial" $ do dir <- ( sampleMavenProjectDir) <$> PIO.getCurrentDir let (dps, dpi) = (mavenPartialScan dir) - res <- callGraphOf dps + res <- run $ callGraphOf dps res `shouldBe'` SourceUnitReachabilitySkippedPartialGraph dpi it' "should return SkippedNotSupported for non-mvn or gradle project" $ do dir <- ( sampleMavenProjectDir) <$> PIO.getCurrentDir let (dps, dpi) = (poetryCompleteScan dir) - res <- callGraphOf dps + res <- run $ callGraphOf dps res `shouldBe'` (SourceUnitReachabilitySkippedNotSupported dpi) analyzeForReachabilitySpec :: Spec @@ -110,12 +115,13 @@ analyzeForReachabilitySpec = it' "should return analyzed reachability unit" $ do dir <- ( sampleMavenProjectDir) <$> PIO.getCurrentDir analyzed <- - analyzeForReachability - [ fst $ skippedProject dir - , fst $ skippedProjectByDefaultFilter dir - , fst $ mavenPartialScan dir - , fst $ poetryCompleteScan dir - ] + run $ + analyzeForReachability + [ fst $ skippedProject dir + , fst $ skippedProjectByDefaultFilter dir + , fst $ mavenPartialScan dir + , fst $ poetryCompleteScan dir + ] (onlyFoundUnits analyzed) `shouldBe'` [] uploadSpec :: Spec diff --git a/test/Reachability/testdata/maven-default-missing-jar/pom.xml b/test/Reachability/testdata/maven-default-missing-jar/pom.xml new file mode 100644 index 0000000000..614ff181d5 --- /dev/null +++ b/test/Reachability/testdata/maven-default-missing-jar/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + com.example + project + 1.0.0 + + + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/test/Test/Fixtures.hs b/test/Test/Fixtures.hs index bf9e45e556..315fa8818d 100644 --- a/test/Test/Fixtures.hs +++ b/test/Test/Fixtures.hs @@ -525,6 +525,7 @@ standardAnalyzeConfig = , ANZ.grepOptions = grepOptions , ANZ.customFossaDepsFile = customFossaDepsFile , ANZ.allowedTacticTypes = Any + , ANZ.reachabilityConfig = mempty } sampleJarParsedContent :: Text