Skip to content

Commit 1bb7e48

Browse files
jerbob92knqyf263
andauthored
Allow to scan a single file (fanal#356)
Co-authored-by: knqyf263 <[email protected]>
1 parent d081855 commit 1bb7e48

File tree

8 files changed

+101
-21
lines changed

8 files changed

+101
-21
lines changed

artifact/local/fs.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"crypto/sha256"
66
"encoding/json"
7-
"io/ioutil"
87
"os"
98
"path/filepath"
109
"strings"
@@ -29,9 +28,9 @@ const (
2928
)
3029

3130
type Artifact struct {
32-
dir string
31+
rootPath string
3332
cache cache.ArtifactCache
34-
walker walker.Dir
33+
walker walker.FS
3534
analyzer analyzer.AnalyzerGroup
3635
hookManager hook.Manager
3736
scanner scanner.Scanner
@@ -40,21 +39,21 @@ type Artifact struct {
4039
configScannerOption config.ScannerOption
4140
}
4241

43-
func NewArtifact(dir string, c cache.ArtifactCache, artifactOpt artifact.Option, scannerOpt config.ScannerOption) (artifact.Artifact, error) {
42+
func NewArtifact(rootPath string, c cache.ArtifactCache, artifactOpt artifact.Option, scannerOpt config.ScannerOption) (artifact.Artifact, error) {
4443
// Register config analyzers
4544
if err := config.RegisterConfigAnalyzers(scannerOpt.FilePatterns); err != nil {
4645
return nil, xerrors.Errorf("config analyzer error: %w", err)
4746
}
4847

49-
s, err := scanner.New(dir, scannerOpt.Namespaces, scannerOpt.PolicyPaths, scannerOpt.DataPaths, scannerOpt.Trace)
48+
s, err := scanner.New(rootPath, scannerOpt.Namespaces, scannerOpt.PolicyPaths, scannerOpt.DataPaths, scannerOpt.Trace)
5049
if err != nil {
5150
return nil, xerrors.Errorf("scanner error: %w", err)
5251
}
5352

5453
return Artifact{
55-
dir: dir,
54+
rootPath: filepath.Clean(rootPath),
5655
cache: c,
57-
walker: walker.NewDir(buildAbsPaths(dir, artifactOpt.SkipFiles), buildAbsPaths(dir, artifactOpt.SkipDirs)),
56+
walker: walker.NewFS(buildAbsPaths(rootPath, artifactOpt.SkipFiles), buildAbsPaths(rootPath, artifactOpt.SkipDirs)),
5857
analyzer: analyzer.NewAnalyzerGroup(artifactOpt.AnalyzerGroup, artifactOpt.DisabledAnalyzers),
5958
hookManager: hook.NewManager(artifactOpt.DisabledHooks),
6059
scanner: s,
@@ -81,21 +80,29 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error)
8180
result := new(analyzer.AnalysisResult)
8281
limit := semaphore.NewWeighted(parallel)
8382

84-
err := a.walker.Walk(a.dir, func(filePath string, info os.FileInfo, opener analyzer.Opener) error {
83+
err := a.walker.Walk(a.rootPath, func(filePath string, info os.FileInfo, opener analyzer.Opener) error {
84+
directory := a.rootPath
85+
86+
// When the directory is the same as the filePath, a file was given
87+
// instead of a directory, rewrite the directory in this case.
88+
if a.rootPath == filePath {
89+
directory = filepath.Dir(a.rootPath)
90+
}
91+
8592
// For exported rootfs (e.g. images/alpine/etc/alpine-release)
86-
filePath, err := filepath.Rel(a.dir, filePath)
93+
filePath, err := filepath.Rel(directory, filePath)
8794
if err != nil {
8895
return xerrors.Errorf("filepath rel (%s): %w", filePath, err)
8996
}
9097

9198
opts := analyzer.AnalysisOptions{Offline: a.artifactOption.Offline}
92-
if err = a.analyzer.AnalyzeFile(ctx, &wg, limit, result, a.dir, filePath, info, opener, opts); err != nil {
99+
if err = a.analyzer.AnalyzeFile(ctx, &wg, limit, result, directory, filePath, info, opener, opts); err != nil {
93100
return xerrors.Errorf("analyze file (%s): %w", filePath, err)
94101
}
95102
return nil
96103
})
97104
if err != nil {
98-
return types.ArtifactReference{}, xerrors.Errorf("walk dir: %w", err)
105+
return types.ArtifactReference{}, xerrors.Errorf("walk filesystem: %w", err)
99106
}
100107

101108
// Wait for all the goroutine to finish.
@@ -144,11 +151,11 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error)
144151

145152
// get hostname
146153
var hostName string
147-
b, err := ioutil.ReadFile(filepath.Join(a.dir, "etc", "hostname"))
154+
b, err := os.ReadFile(filepath.Join(a.rootPath, "etc", "hostname"))
148155
if err == nil && string(b) != "" {
149156
hostName = strings.TrimSpace(string(b))
150157
} else {
151-
hostName = a.dir
158+
hostName = a.rootPath
152159
}
153160

154161
return types.ArtifactReference{

artifact/local/fs_test.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestArtifact_Inspect(t *testing.T) {
3636
{
3737
name: "happy path",
3838
fields: fields{
39-
dir: "./testdata",
39+
dir: "./testdata/alpine",
4040
},
4141
putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
4242
Args: cache.ArtifactCachePutBlobArgs{
@@ -72,7 +72,7 @@ func TestArtifact_Inspect(t *testing.T) {
7272
{
7373
name: "disable analyzers",
7474
fields: fields{
75-
dir: "./testdata",
75+
dir: "./testdata/alpine",
7676
},
7777
artifactOpt: artifact.Option{
7878
DisabledAnalyzers: []analyzer.Type{analyzer.TypeAlpine, analyzer.TypeApk},
@@ -99,7 +99,7 @@ func TestArtifact_Inspect(t *testing.T) {
9999
{
100100
name: "sad path PutBlob returns an error",
101101
fields: fields{
102-
dir: "./testdata",
102+
dir: "./testdata/alpine",
103103
},
104104
putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
105105
Args: cache.ArtifactCachePutBlobArgs{
@@ -134,6 +134,78 @@ func TestArtifact_Inspect(t *testing.T) {
134134
},
135135
wantErr: "no such file or directory",
136136
},
137+
{
138+
name: "happy path with single file",
139+
fields: fields{
140+
dir: "testdata/requirements.txt",
141+
},
142+
putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
143+
Args: cache.ArtifactCachePutBlobArgs{
144+
BlobID: "sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
145+
BlobInfo: types.BlobInfo{
146+
SchemaVersion: types.BlobJSONSchemaVersion,
147+
DiffID: "sha256:de52b03af926ba8f646bd11b794f014161b11a3dbad0213d556ea9af120e1623",
148+
Applications: []types.Application{
149+
{
150+
Type: "pip",
151+
FilePath: "requirements.txt",
152+
Libraries: []types.Package{
153+
{
154+
Name: "Flask",
155+
Version: "2.0.0",
156+
},
157+
},
158+
},
159+
},
160+
},
161+
},
162+
Returns: cache.ArtifactCachePutBlobReturns{},
163+
},
164+
want: types.ArtifactReference{
165+
Name: "testdata/requirements.txt",
166+
Type: types.ArtifactFilesystem,
167+
ID: "sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
168+
BlobIDs: []string{
169+
"sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
170+
},
171+
},
172+
},
173+
{
174+
name: "happy path with single file using relative path",
175+
fields: fields{
176+
dir: "./testdata/requirements.txt",
177+
},
178+
putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
179+
Args: cache.ArtifactCachePutBlobArgs{
180+
BlobID: "sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
181+
BlobInfo: types.BlobInfo{
182+
SchemaVersion: types.BlobJSONSchemaVersion,
183+
DiffID: "sha256:de52b03af926ba8f646bd11b794f014161b11a3dbad0213d556ea9af120e1623",
184+
Applications: []types.Application{
185+
{
186+
Type: "pip",
187+
FilePath: "requirements.txt",
188+
Libraries: []types.Package{
189+
{
190+
Name: "Flask",
191+
Version: "2.0.0",
192+
},
193+
},
194+
},
195+
},
196+
},
197+
},
198+
Returns: cache.ArtifactCachePutBlobReturns{},
199+
},
200+
want: types.ArtifactReference{
201+
Name: "testdata/requirements.txt",
202+
Type: types.ArtifactFilesystem,
203+
ID: "sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
204+
BlobIDs: []string{
205+
"sha256:66ba8d6fd07f032638ec8ee517f85b3aad8c6b263c9b1f784237511558002851",
206+
},
207+
},
208+
},
137209
}
138210
for _, tt := range tests {
139211
t.Run(tt.name, func(t *testing.T) {
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Flask==2.0.0

walker/fs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import (
1010
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
1111
)
1212

13-
type Dir struct {
13+
type FS struct {
1414
walker
1515
}
1616

17-
func NewDir(skipFiles, skipDirs []string) Dir {
18-
return Dir{
17+
func NewFS(skipFiles, skipDirs []string) FS {
18+
return FS{
1919
walker: newWalker(skipFiles, skipDirs),
2020
}
2121
}
2222

2323
// Walk walks the file tree rooted at root, calling WalkFunc for each file or
2424
// directory in the tree, including root, but a directory to be ignored will be skipped.
25-
func (w Dir) Walk(root string, fn WalkFunc) error {
25+
func (w FS) Walk(root string, fn WalkFunc) error {
2626
// walk function called for every path found
2727
walkFn := func(pathname string, fi os.FileInfo) error {
2828
pathname = filepath.Clean(pathname)

walker/fs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func TestDir_Walk(t *testing.T) {
7979
}
8080
for _, tt := range tests {
8181
t.Run(tt.name, func(t *testing.T) {
82-
w := walker.NewDir(tt.fields.skipFiles, tt.fields.skipDirs)
82+
w := walker.NewFS(tt.fields.skipFiles, tt.fields.skipDirs)
8383

8484
err := w.Walk(tt.rootDir, tt.analyzeFn)
8585
if tt.wantErr != "" {

0 commit comments

Comments
 (0)