Skip to content

Commit db46e95

Browse files
authored
[ane-2575] scan all layers for os info (#1566)
* scan all layers for os info * add test * lint * accidently on purposed * whitespace * typo * update changelog * no need to log * prepare for release
1 parent 9cf32d1 commit db46e95

File tree

5 files changed

+74
-20
lines changed

5 files changed

+74
-20
lines changed

Changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# FOSSA CLI Changelog
22

3-
## Unreleased
3+
## 3.10.14
44

55
- gradle: Do not report version constraints, version contraints are contained within an`DependencyResult`, filter out any constraints by checking [`isConstraint()`](https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/result/DependencyResult.html#isConstraint()). ([#1563](https://github.com/fossas/fossa-cli/pull/1563))
6+
- container scanning: search all layers for os information. ([#1566](https://github.com/fossas/fossa-cli/pull/1566))
67

78
## 3.10.13
89

src/App/Fossa/Container/Sources/DockerArchive.hs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,14 @@ analyzeFromDockerArchive systemDepsOnly filters withoutDefaultFilters tarball =
120120

121121
-- Analyze Base Layer
122122
logInfo "Analyzing Base Layer"
123-
baseFs <- context "Building base layer FS" $ mkFsFromChangeset $ baseLayer image
124123
let imageBaseLayer = baseLayer image
125124
baseDigest = layerDigest imageBaseLayer
126-
osInfo <-
127-
context "Retrieving OS Information"
128-
. warnThenRecover @Text "Could not retrieve OS info"
129-
$ runTarballReadFSIO baseFs tarball getOsInfo
125+
baseFs <- context "Building Base Layer FS" $ mkFsFromChangeset $ baseLayer image
126+
layersFs <-
127+
if hasOtherLayers image
128+
then pure <$> context "Building Other Layers FS" (mkFsFromChangeset (otherLayersSquashed image))
129+
else pure Nothing
130+
osInfo <- getOsInfoFromLayers layersFs baseFs tarball
130131

131132
when (isNothing osInfo) $
132133
logInfo "No image system information detected. System dependencies will not be included with this scan."
@@ -163,11 +164,12 @@ analyzeFromDockerArchive systemDepsOnly filters withoutDefaultFilters tarball =
163164

164165
let baseScanImageLayer = ContainerScanImageLayer baseDigest baseUnits baseObservations
165166

166-
if hasOtherLayers image
167-
then do
167+
case layersFs of
168+
Nothing -> do
169+
pure $ mkScan [baseScanImageLayer]
170+
Just fs -> do
168171
logInfo "Analyzing Other Layers"
169172
let squashedDigest = layerDigest . otherLayersSquashed $ image
170-
fs <- context "Building squashed FS from other layers" . mkFsFromChangeset $ otherLayersSquashed image
171173
otherUnits <-
172174
context "Analyzing from Other Layers" $
173175
analyzeLayer systemDepsOnly filters withoutDefaultFilters capabilities osInfo fs tarball
@@ -178,7 +180,6 @@ analyzeFromDockerArchive systemDepsOnly filters withoutDefaultFilters tarball =
178180
, ContainerScanImageLayer squashedDigest otherUnits otherObservations
179181
]
180182
pure scan
181-
else pure $ mkScan [baseScanImageLayer]
182183

183184
analyzeLayer ::
184185
( Has Diagnostics sig m
@@ -338,17 +339,20 @@ listTargetsFromDockerArchive tarball = do
338339
logWarn "fossa container list-targets only lists targets for experimental-scanner (when analyzed with --experimental-scanner flag)."
339340

340341
logInfo "Analyzing Base Layer"
341-
baseFs <- context "Building Base Layer FS" $ mkFsFromChangeset $ baseLayer image
342-
osInfo <-
343-
context "Retrieving OS Information"
344-
. warnThenRecover @Text "Could not retrieve OS info"
345-
$ runTarballReadFSIO baseFs tarball getOsInfo
342+
343+
baseFs <- mkFsFromChangeset $ baseLayer image
344+
layersFs <-
345+
if hasOtherLayers image
346+
then pure <$> context "Building squashed FS from other layers" (mkFsFromChangeset $ otherLayersSquashed image)
347+
else pure Nothing
348+
osInfo <- getOsInfoFromLayers layersFs baseFs tarball
349+
346350
context "Analyzing From Base Layer" $ listTargetLayer capabilities osInfo baseFs tarball "Base Layer"
347351

348-
when (hasOtherLayers image) $ do
349-
logInfo "Analyzing Other Layers"
350-
fs <- context "Building squashed FS from other layers" $ mkFsFromChangeset $ otherLayersSquashed image
351-
void . context "Analyzing from Other Layers" $ listTargetLayer capabilities osInfo fs tarball "Other Layers"
352+
case layersFs of
353+
Nothing -> pure ()
354+
Just fs -> do
355+
void . context "Analyzing from Other Layers" $ listTargetLayer capabilities osInfo fs tarball "Other Layers"
352356

353357
listTargetLayer ::
354358
( Has Diagnostics sig m
@@ -384,3 +388,25 @@ listTargetLayer capabilities osInfo layerFs tarball layerType = do
384388
run = traverse_ findTargets $ layerAnalyzers osInfo False
385389
findTargets (DiscoverFunc f) = withDiscoveredProjects f basedir (renderLayerTarget basedir layerType)
386390
basedir = Path $ toString fixedVfsRoot
391+
392+
-- | Retrieves OS information from the layers of a Docker image.
393+
-- It first checks the squashed layers, then the base layer.
394+
-- This is because layers are squashed in the order they are applied,
395+
-- so the last layer is the one that should have the most accurate OS info.
396+
getOsInfoFromLayers ::
397+
( Has Diagnostics sig m
398+
, Has (Lift IO) sig m
399+
) =>
400+
Maybe (SomeFileTree TarEntryOffset) ->
401+
SomeFileTree TarEntryOffset ->
402+
Path Abs File ->
403+
m (Maybe OsInfo)
404+
getOsInfoFromLayers layersFs baseFs tarball =
405+
context "Retrieving OS Information" $ do
406+
layersFSOsInfo <- case layersFs of
407+
Just fs -> warnThenRecover @Text "Could not retrieve OS info from other layers, falling back to base layer." $ runTarballReadFSIO fs tarball getOsInfo
408+
Nothing -> pure Nothing
409+
if isNothing layersFSOsInfo
410+
then do
411+
warnThenRecover @Text "Could not retrieve OS info from base layer" $ runTarballReadFSIO baseFs tarball getOsInfo
412+
else pure layersFSOsInfo

test/App/Fossa/Container/AnalyzeNativeSpec.hs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import App.Fossa.Container.Scan (
77
parseDockerEngineSource,
88
)
99
import App.Fossa.Container.Sources.DockerArchive (analyzeFromDockerArchive)
10-
import Container.Types (ContainerScan (ContainerScan, imageData), ContainerScanImage (ContainerScanImage, imageLayers), layerId, observations, srcUnits)
10+
import Container.Types (ContainerScan (ContainerScan, imageData), ContainerScanImage (ContainerScanImage, imageLayers, imageOs), layerId, observations, srcUnits)
1111
import Control.Carrier.Debug (IgnoreDebugC, ignoreDebug)
1212
import Control.Carrier.Diagnostics (DiagnosticsC, Has)
1313
import Control.Carrier.Stack (StackC, runStack)
@@ -81,6 +81,7 @@ analyzeSpec :: Spec
8181
analyzeSpec = describe "analyze" $ do
8282
currDir <- runIO getCurrentDir
8383
let imageArchive = currDir </> appDepsImage
84+
let osInfoArchive = currDir </> osInfoImage
8485

8586
it' "should not analyze application dependencies when only-system-dependencies are requested" $ do
8687
containerScan <- analyzeFromDockerArchive True mempty (toFlag' False) imageArchive
@@ -111,6 +112,11 @@ analyzeSpec = describe "analyze" $ do
111112
buildImportsOf containerScan `shouldNotContain'` [black]
112113
buildImportsOf containerScan `shouldBeSupersetOf'` [numpy, scipy]
113114

115+
it' "should get os-info from any layer" $ do
116+
containerScan <- analyzeFromDockerArchive False mempty (toFlag' False) osInfoArchive
117+
let osInfo = imageData containerScan
118+
(imageOs osInfo) `shouldBe'` Just "fakeos"
119+
114120
buildImportsOf :: ContainerScan -> [Locator]
115121
buildImportsOf scan =
116122
concatMap buildImports $
@@ -156,6 +162,9 @@ runWithMockDockerEngineEff =
156162
appDepsImage :: Path Rel File
157163
appDepsImage = $(mkRelFile "test/Container/testdata/app_deps_example.tar")
158164

165+
osInfoImage :: Path Rel File
166+
osInfoImage = $(mkRelFile "test/Container/testdata/osinfo_example.tar")
167+
159168
-- | From "app/services/a/" project.
160169
numpy :: Locator
161170
numpy = Locator "pip" "numpy" Nothing
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Testcase: osinfo_example.tar
2+
#
3+
# To Build:
4+
# docker build -f Dockerfile.osinfo.sample . -t osinfo_example:latest
5+
#
6+
# To Export:
7+
# docker save osinfo_example:latest > osinfo_example.tar
8+
#
9+
# To Run:
10+
# docker run -it osinfo_example:latest /bin/sh
11+
FROM busybox
12+
13+
# Generate fake os-release file
14+
RUN echo "NAME=\"Fake OS\"" > /etc/os-release && \
15+
echo "VERSION=\"1.0\"" >> /etc/os-release && \
16+
echo "ID=fakeos" >> /etc/os-release && \
17+
echo "VERSION_ID=\"1.0\"" >> /etc/os-release && \
18+
echo "PRETTY_NAME=\"Fake OS 1.0\"" >> /etc/os-release
4.1 MB
Binary file not shown.

0 commit comments

Comments
 (0)