@@ -24,6 +24,8 @@ import App.Fossa.Ficus.Types (
2424 FicusPerStrategyFlag (.. ),
2525 FicusScanStats (.. ),
2626 FicusSnippetScanResults (.. ),
27+ FicusVendoredDependency (.. ),
28+ FicusVendoredDependencyScanResults (.. ),
2729 )
2830import App.Types (ProjectRevision (.. ))
2931import Control.Applicative ((<|>) )
@@ -32,6 +34,7 @@ import Control.Concurrent.Async (async, wait)
3234import Control.Effect.Lift (Has , Lift , sendIO )
3335import Control.Monad (when )
3436import Data.Aeson (decode , decodeStrictText )
37+ import Data.Aeson qualified as Aeson
3538import Data.ByteString.Lazy qualified as BL
3639import Data.Conduit ((.|) )
3740import Data.Conduit qualified as Conduit
@@ -51,7 +54,7 @@ import Effect.Logger (Logger, logDebug, logInfo)
5154import Fossa.API.Types (ApiKey (.. ), ApiOpts (.. ))
5255import Path (Abs , Dir , Path , toFilePath )
5356import Prettyprinter (pretty )
54- import Srclib.Types (Locator (.. ), renderLocator )
57+ import Srclib.Types (Locator (.. ), SourceUnit ( .. ), SourceUnitBuild ( .. ), SourceUnitDependency ( .. ), renderLocator , textToOriginPath )
5558import System.FilePath ((</>) )
5659import System.IO (Handle , IOMode (WriteMode ), hClose , hGetLine , hIsEOF , hPutStrLn , openFile , stderr )
5760import System.Process.Typed (
@@ -68,7 +71,7 @@ import System.Process.Typed (
6871import Text.Printf (printf )
6972import Text.URI (render )
7073import Text.URI.Builder (PathComponent (PathComponent ), TrailingSlash (TrailingSlash ), setPath )
71- import Types (GlobFilter (.. ), LicenseScanPathFilters (.. ))
74+ import Types (GlobFilter (.. ), GraphBreadth ( .. ), LicenseScanPathFilters (.. ))
7275import Prelude
7376
7477newtype CustomLicensePath = CustomLicensePath { unCustomLicensePath :: Text }
@@ -166,6 +169,55 @@ formatFicusScanSummary results =
166169 formatProcessingTime :: Double -> Text
167170 formatProcessingTime seconds = toText (printf " %.3f" seconds :: String )
168171
172+ findingToVendoredDependency :: FicusFinding -> Maybe FicusVendoredDependency
173+ findingToVendoredDependency (FicusFinding (FicusMessageData strategy payload))
174+ | Text. toLower strategy == " vendored" =
175+ decode (BL. fromStrict $ Text.Encoding. encodeUtf8 payload)
176+ findingToVendoredDependency _ = Nothing
177+
178+ vendoredDepsToSourceUnit :: [FicusVendoredDependency ] -> SourceUnit
179+ vendoredDepsToSourceUnit deps =
180+ SourceUnit
181+ { sourceUnitName = " ficus-vendored-dependencies"
182+ , sourceUnitType = " ficus-vendored"
183+ , sourceUnitManifest = " ficus-vendored-dependencies"
184+ , sourceUnitBuild =
185+ Just $
186+ SourceUnitBuild
187+ { buildArtifact = " default"
188+ , buildSucceeded = True
189+ , buildImports = locators
190+ , buildDependencies = dependencies
191+ }
192+ , sourceUnitGraphBreadth = Complete
193+ , sourceUnitNoticeFiles = []
194+ , sourceUnitOriginPaths = map (textToOriginPath . ficusVendoredDependencyPath) deps
195+ , sourceUnitLabels = Nothing
196+ , additionalData = Nothing
197+ }
198+ where
199+ locators :: [Locator ]
200+ locators = map vendoredDepToLocator deps
201+
202+ dependencies :: [SourceUnitDependency ]
203+ dependencies = map vendoredDepToSourceUnitDependency deps
204+
205+ vendoredDepToLocator :: FicusVendoredDependency -> Locator
206+ vendoredDepToLocator dep =
207+ Locator
208+ { locatorFetcher = ficusVendoredDependencyEcosystem dep
209+ , locatorProject = ficusVendoredDependencyName dep
210+ , locatorRevision = ficusVendoredDependencyVersion dep
211+ }
212+
213+ vendoredDepToSourceUnitDependency :: FicusVendoredDependency -> SourceUnitDependency
214+ vendoredDepToSourceUnitDependency dep =
215+ SourceUnitDependency
216+ { sourceDepLocator = vendoredDepToLocator dep
217+ , sourceDepImports = []
218+ , sourceDepData = Aeson. object [" path" Aeson. .= ficusVendoredDependencyPath dep]
219+ }
220+
169221runFicus ::
170222 ( Has Diagnostics sig m
171223 , Has (Lift IO ) sig m
@@ -238,39 +290,56 @@ runFicus maybeDebugDir ficusConfig = do
238290 pure . formatTime defaultTimeLocale " %H:%M:%S.%3q" $ now
239291
240292 streamFicusOutput :: Handle -> Maybe Handle -> IO FicusAnalysisResults
241- streamFicusOutput handle maybeFile =
242- Conduit. runConduit $
243- CC. sourceHandle handle
244- .| CC. decodeUtf8Lenient
245- .| CC. linesUnbounded
246- .| CC. mapM
247- ( \ line -> do
248- -- Tee raw line to file if debug mode
249- traverse_ (\ fileH -> hPutStrLn fileH (toString line)) maybeFile
250- pure line
251- )
252- .| CCL. mapMaybe decodeStrictText
253- .| CC. foldM
254- ( \ acc message -> do
255- -- Log messages as they come, with timestamps
256- timestamp <- currentTimeStamp
257- case message of
258- FicusMessageError err -> do
259- hPutStrLn stderr $ " [" ++ timestamp ++ " ] ERROR " <> toString (displayFicusError err)
260- pure acc
261- FicusMessageDebug dbg -> do
262- hPutStrLn stderr $ " [" ++ timestamp ++ " ] DEBUG " <> toString (displayFicusDebug dbg)
263- pure acc
264- FicusMessageFinding finding -> do
265- hPutStrLn stderr $ " [" ++ timestamp ++ " ] FINDING " <> toString (displayFicusFinding finding)
266- let analysisFinding = findingToSnippetScanResult finding
267- let currentSnippetResults = snippetScanResults acc
268- when (isJust currentSnippetResults && isJust analysisFinding) $
269- hPutStrLn stderr $
270- " [" ++ timestamp ++ " ] ERROR " <> " Found multiple ficus analysis responses."
271- pure $ acc{snippetScanResults = currentSnippetResults <|> analysisFinding}
272- )
273- (FicusAnalysisResults {snippetScanResults = Nothing , vendoredDependencyScanResults = Nothing })
293+ streamFicusOutput handle maybeFile = do
294+ accumulator <-
295+ Conduit. runConduit $
296+ CC. sourceHandle handle
297+ .| CC. decodeUtf8Lenient
298+ .| CC. linesUnbounded
299+ .| CC. mapM
300+ ( \ line -> do
301+ -- Tee raw line to file if debug mode
302+ traverse_ (\ fileH -> hPutStrLn fileH (toString line)) maybeFile
303+ pure line
304+ )
305+ .| CCL. mapMaybe decodeStrictText
306+ .| CC. foldM
307+ ( \ acc message -> do
308+ -- Log messages as they come, with timestamps
309+ timestamp <- currentTimeStamp
310+ case message of
311+ FicusMessageError err -> do
312+ hPutStrLn stderr $ " [" ++ timestamp ++ " ] ERROR " <> toString (displayFicusError err)
313+ pure acc
314+ FicusMessageDebug dbg -> do
315+ hPutStrLn stderr $ " [" ++ timestamp ++ " ] DEBUG " <> toString (displayFicusDebug dbg)
316+ pure acc
317+ FicusMessageFinding finding -> do
318+ hPutStrLn stderr $ " [" ++ timestamp ++ " ] FINDING " <> toString (displayFicusFinding finding)
319+ let analysisFinding = findingToSnippetScanResult finding
320+ let vendoredDep = findingToVendoredDependency finding
321+ let (currentSnippetResults, currentVendoredDeps) = acc
322+ when (isJust currentSnippetResults && isJust analysisFinding) $
323+ hPutStrLn stderr $
324+ " [" ++ timestamp ++ " ] ERROR " <> " Found multiple ficus analysis responses."
325+ let newSnippetResults = currentSnippetResults <|> analysisFinding
326+ let newVendoredDeps = case vendoredDep of
327+ Just dep -> dep : currentVendoredDeps
328+ Nothing -> currentVendoredDeps
329+ pure (newSnippetResults, newVendoredDeps)
330+ )
331+ (Nothing , [] )
332+
333+ let (snippetResults, vendoredDeps) = accumulator
334+ let vendoredResults = case vendoredDeps of
335+ [] -> Nothing
336+ deps -> Just $ FicusVendoredDependencyScanResults (Just $ vendoredDepsToSourceUnit deps)
337+
338+ pure $
339+ FicusAnalysisResults
340+ { snippetScanResults = snippetResults
341+ , vendoredDependencyScanResults = vendoredResults
342+ }
274343
275344 consumeStderr :: Handle -> Maybe Handle -> IO [Text ]
276345 consumeStderr handle maybeFile = do
0 commit comments