Skip to content

Commit 552c4de

Browse files
committed
Initial commit
1 parent 7b3bf98 commit 552c4de

File tree

7 files changed

+260
-0
lines changed

7 files changed

+260
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13+
.idea

analyzer/analyzer.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package analyzer
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/knqyf263/fanal/extractor"
8+
"github.com/pkg/errors"
9+
)
10+
11+
var (
12+
osAnalyzers []OSAnalyzer
13+
pkgAnalyzers []PkgAnalyzer
14+
15+
// ErrUnknownOS occurs when unknown OS is analyzed.
16+
ErrUnknownOS = errors.New("Unknown OS")
17+
// ErrPkgAnalysis occurs when the analysis of packages is failed.
18+
ErrPkgAnalysis = errors.New("Failed to analyze packages")
19+
)
20+
21+
type OSAnalyzer interface {
22+
Analyze(extractor.FilesMap) (OS, error)
23+
RequiredFiles() []string
24+
}
25+
26+
type PkgAnalyzer interface {
27+
Analyze(extractor.FilesMap) ([]Package, error)
28+
RequiredFiles() []string
29+
}
30+
31+
type OS struct {
32+
Name string
33+
Family string
34+
}
35+
36+
type Package struct {
37+
Name string
38+
Version string
39+
Release string
40+
Epoch int
41+
}
42+
43+
func RegisterOSAnalyzer(analyzer OSAnalyzer) {
44+
osAnalyzers = append(osAnalyzers, analyzer)
45+
}
46+
47+
func RegisterPkgAnalyzer(analyzer PkgAnalyzer) {
48+
pkgAnalyzers = append(pkgAnalyzers, analyzer)
49+
}
50+
51+
func RequiredFilenames() []string {
52+
filenames := []string{}
53+
for _, analyzer := range osAnalyzers {
54+
filenames = append(filenames, analyzer.RequiredFiles()...)
55+
}
56+
for _, analyzer := range pkgAnalyzers {
57+
filenames = append(filenames, analyzer.RequiredFiles()...)
58+
}
59+
return filenames
60+
}
61+
62+
func Analyze(dir string) (filesMap extractor.FilesMap, err error) {
63+
extractor := extractor.DockerExtractor{}
64+
file, _ := os.Open(filepath.Join(dir, "layer.tar"))
65+
66+
filesMap, err = extractor.ExtractFiles(file, RequiredFilenames())
67+
if err != nil {
68+
return nil, errors.Wrap(err, "Failed to extract files")
69+
}
70+
return filesMap, nil
71+
}
72+
73+
func GetOS(filesMap extractor.FilesMap) (OS, error) {
74+
for _, analyzer := range osAnalyzers {
75+
os, err := analyzer.Analyze(filesMap)
76+
if err != nil {
77+
continue
78+
}
79+
return os, nil
80+
}
81+
return OS{}, ErrUnknownOS
82+
83+
}
84+
85+
func GetPackages(filesMap extractor.FilesMap) ([]Package, error) {
86+
for _, analyzer := range pkgAnalyzers {
87+
pkgs, err := analyzer.Analyze(filesMap)
88+
if err != nil {
89+
continue
90+
}
91+
return pkgs, nil
92+
}
93+
return nil, ErrUnknownOS
94+
95+
}

analyzer/os/alpine/alpine.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package alpine
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"errors"
7+
"fmt"
8+
9+
"github.com/knqyf263/fanal/analyzer"
10+
"github.com/knqyf263/fanal/extractor"
11+
)
12+
13+
func init() {
14+
analyzer.RegisterOSAnalyzer(&alpineOSAnalyzer{})
15+
}
16+
17+
type alpineOSAnalyzer struct{}
18+
19+
func (a alpineOSAnalyzer) Analyze(filesMap extractor.FilesMap) (analyzer.OS, error) {
20+
for _, filename := range a.RequiredFiles() {
21+
file, ok := filesMap[filename]
22+
if !ok {
23+
continue
24+
}
25+
scanner := bufio.NewScanner(bytes.NewBuffer(file))
26+
for scanner.Scan() {
27+
// TODO
28+
line := scanner.Text()
29+
fmt.Println(line)
30+
}
31+
}
32+
return analyzer.OS{}, errors.New("alpine: Not match")
33+
}
34+
35+
func (a alpineOSAnalyzer) RequiredFiles() []string {
36+
return []string{"etc/alpine-release"}
37+
}

analyzer/pkg/apk/apk.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package apk
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
8+
"github.com/knqyf263/fanal/analyzer"
9+
"github.com/knqyf263/fanal/extractor"
10+
"github.com/pkg/errors"
11+
)
12+
13+
func init() {
14+
analyzer.RegisterPkgAnalyzer(&alpinePkgAnalyzer{})
15+
}
16+
17+
type alpinePkgAnalyzer struct{}
18+
19+
func (a alpinePkgAnalyzer) Analyze(filesMap extractor.FilesMap) ([]analyzer.Package, error) {
20+
for _, filename := range a.RequiredFiles() {
21+
file, ok := filesMap[filename]
22+
if !ok {
23+
continue
24+
}
25+
scanner := bufio.NewScanner(bytes.NewBuffer(file))
26+
for scanner.Scan() {
27+
// TODO
28+
line := scanner.Text()
29+
fmt.Println(line)
30+
}
31+
}
32+
return []analyzer.Package{}, errors.New("alpine: Not match")
33+
}
34+
35+
func (a alpinePkgAnalyzer) RequiredFiles() []string {
36+
return []string{"lib/apk/db/installed"}
37+
}

cmd/fanal/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/knqyf263/fanal/analyzer"
8+
_ "github.com/knqyf263/fanal/analyzer/os/alpine"
9+
_ "github.com/knqyf263/fanal/analyzer/pkg/apk"
10+
)
11+
12+
func main() {
13+
dir, err := os.Getwd()
14+
if err != nil {
15+
log.Fatal(err)
16+
}
17+
18+
files, _ := analyzer.Analyze(dir)
19+
analyzer.GetOS(files)
20+
analyzer.GetPackages(files)
21+
}

extractor/docker.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package extractor
2+
3+
import (
4+
"archive/tar"
5+
"io"
6+
"io/ioutil"
7+
"path"
8+
)
9+
10+
type DockerExtractor struct{}
11+
12+
func (d DockerExtractor) ExtractFiles(layer io.ReadCloser, filenames []string) (FilesMap, error) {
13+
data := make(map[string][]byte)
14+
15+
tr := tar.NewReader(layer)
16+
for {
17+
hdr, err := tr.Next()
18+
if err == io.EOF {
19+
break
20+
}
21+
if err != nil {
22+
return data, ErrCouldNotExtract
23+
}
24+
25+
// Get element filename
26+
filename := hdr.Name
27+
filename = path.Clean(filename)
28+
29+
// Determine if we should extract the element
30+
extract := false
31+
for _, s := range filenames {
32+
if s == filename {
33+
extract = true
34+
break
35+
}
36+
}
37+
38+
if !extract {
39+
continue
40+
}
41+
42+
// Extract the element
43+
if hdr.Typeflag == tar.TypeSymlink || hdr.Typeflag == tar.TypeLink || hdr.Typeflag == tar.TypeReg {
44+
d, _ := ioutil.ReadAll(tr)
45+
data[filename] = d
46+
}
47+
}
48+
49+
return data, nil
50+
51+
}

extractor/extractor.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package extractor
2+
3+
import (
4+
"io"
5+
6+
"github.com/pkg/errors"
7+
)
8+
9+
var (
10+
// ErrCouldNotExtract occurs when an extraction fails.
11+
ErrCouldNotExtract = errors.New("Could not extract the archive")
12+
)
13+
14+
type FilesMap map[string][]byte
15+
16+
type Extractor interface {
17+
ExtractFiles(layer io.ReadCloser, filenames []string) (FilesMap, error)
18+
}

0 commit comments

Comments
 (0)