Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Photon OS driver #886

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/clair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import (
_ "github.com/quay/clair/v3/ext/vulnsrc/amzn"
_ "github.com/quay/clair/v3/ext/vulnsrc/debian"
_ "github.com/quay/clair/v3/ext/vulnsrc/oracle"
_ "github.com/quay/clair/v3/ext/vulnsrc/photon"
_ "github.com/quay/clair/v3/ext/vulnsrc/rhel"
_ "github.com/quay/clair/v3/ext/vulnsrc/suse"
_ "github.com/quay/clair/v3/ext/vulnsrc/ubuntu"
Expand Down
1 change: 1 addition & 0 deletions config.yaml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ clair:
- oracle
- alpine
- suse
- photon

notifier:
# Number of attempts before the notification is marked as failed to be sent
Expand Down
50 changes: 50 additions & 0 deletions ext/vulnsrc/photon/download_utilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package photon

import (
"io/ioutil"
"net/http"

"github.com/quay/clair/v3/pkg/commonerr"
"github.com/quay/clair/v3/pkg/httputil"
log "github.com/sirupsen/logrus"
)

// downloadFile downloads a particular file and checks
// wherever the download succeeded
func downloadFile(URL string) (response *http.Response, err error) {
response, err = httputil.GetWithUserAgent(URL)
if err != nil {
log.WithError(err).Errorf("could not download the file %v", URL)
return nil, commonerr.ErrCouldNotDownload
}
if !httputil.Status2xx(response) {
log.WithField("StatusCode", response.StatusCode).Errorf("Failed to download download the file %v", URL)
return nil, commonerr.ErrCouldNotDownload
}
return
}

// downloadPhotonCVEfiles downloads all cve metadata files
// for all versions provided in the versions slice
func downloadPhotonCVEfiles(versions []string) (responses map[string][]byte, err error) {
responses = make(map[string][]byte)

for _, version := range versions {
URL := cveFilesURLlprefix + version + ".json"

response, err := downloadFile(URL)
if err != nil {
log.Infof("Problems downloading file from link: %v\n Skipping Photon version %v", URL, version)
continue
}
defer response.Body.Close()

byteArr, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Infof("Problems reading response body!\n Skipping Photon version %v", version)
continue
}
responses[version] = byteArr
}
return
}
106 changes: 106 additions & 0 deletions ext/vulnsrc/photon/hashes_utilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package photon

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"sort"
"strings"

"github.com/quay/clair/v3/pkg/commonerr"
log "github.com/sirupsen/logrus"
)

// calculateHash calculates sha256 byte slice
// in io.Reader containing bytes information
func calculateHash(jsonReader io.Reader) (sha string, err error) {
// Create a TeeReader so that we can unmarshal into JSON and write to a hash
// digest at the same time.
jsonSHA := sha256.New()
teedJSONReader := io.TeeReader(jsonReader, jsonSHA)

// Unmarshal JSON.
var data []cve
err = json.NewDecoder(teedJSONReader).Decode(&data)
if err != nil {
log.WithError(err).Error("could not unmarshal Photon's JSON")
return "", commonerr.ErrCouldNotParse
}
hash := hex.EncodeToString(jsonSHA.Sum(nil))
return hash, nil
}

// calculateNewCVEfilesHashes returns a map which has photon version for a key
// and a hash value of the data corresponding cve metadata as a value
func calculateNewHashes(responses map[string][]byte) (versionToHash map[string]string) {
versionToHash = make(map[string]string, 5)

for version := range responses {
byteReader := bytes.NewReader(responses[version])
newHash, err := calculateHash(byteReader)
if err != nil {
log.WithField("package", "Photon").Infof("Problem calculating the hash for version %v", version)
newHash = ""
} else {
versionToHash[version] = newHash
}
}
return
}

// extractOldHashes creates a map from latestHashes
// containing photon versions as keys and hashes
// of their corresponding cve metadata files
func extractOldHashes(latestHashes string) (versionToHash map[string]string) {
if latestHashes == "" {
return nil
}
versionToHash = make(map[string]string, 5)
tuples := strings.Split(latestHashes, ";")

for _, tuple := range tuples {
if tuple == "" {
continue
}

keyHashTuple := strings.Split(tuple, ":")
versionToHash[keyHashTuple[0]] = keyHashTuple[1]
}
return versionToHash
}

// createNewUpdaterFlag creates new updater flag
// it takes all old versions and hashes and updates
// those versions in the versionsToBeUpdated slice
func createNewUpdaterFlag(oldVersionToHash map[string]string, newVersionToHash map[string]string,
versionsToBeUpdated []string) (updaterFlag string) {

if oldVersionToHash == nil && newVersionToHash == nil {
return ""
}
updatedVersionHashes := make(map[string]string)
// Initialize the updatedVersionHashes so it will
// have infomation for all hashes of all previous cve files
for oldVer, oldHash := range oldVersionToHash {
updatedVersionHashes[oldVer] = oldHash
}

for _, version := range versionsToBeUpdated {
updatedVersionHashes[version] = newVersionToHash[version]
}
// Sort version slice will be use to access the
// updatedVersionHashes map in a sorted way
sortVersions := make([]string, 0)
for version, _ := range updatedVersionHashes {
sortVersions = append(sortVersions, version)
}
sort.Strings(sortVersions)

for _, version := range sortVersions {
updaterFlag += fmt.Sprintf("%v:%v;", version, updatedVersionHashes[version])
}
return
}
175 changes: 175 additions & 0 deletions ext/vulnsrc/photon/hashes_utilities_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package photon

import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
)

func TestCalclateHash(t *testing.T) {
testFilePath := "/testdata/cve_metadata.json"
_, filename, _, _ := runtime.Caller(0)
testFile, _ := os.Open(filepath.Join(filepath.Dir(filename), testFilePath))

expectedHash := "b4a1877bbf70e861d3868fe65282b7183f6f4ce29aea057a0b8ac14ff5e1b9c5"
receivedHash, err := calculateHash(testFile)
if err != nil {
assert.Fail(t, "Calculating the %v file hash failed with error %v!", testFilePath, err)
}
if receivedHash != expectedHash {
assert.Fail(t, "Caluclated has of the file is not correct!", "Want: %v \n Have: %v \n",
expectedHash, receivedHash)
}
}

func readTestFile(currentFile, testFileName string) (data []byte, err error) {
testFilePath := filepath.Join(filepath.Dir(currentFile), "/testdata/", testFileName)
testFile, _ := os.Open(testFilePath)
byteArr, err := ioutil.ReadAll(testFile)
if err != nil {
return nil, err
}
return byteArr, nil
}

func TestCalculateNewCVEfilesHashes(t *testing.T) {
_, currentFile, _, _ := runtime.Caller(0)

byteArr1, err := readTestFile(currentFile, "cve_metadata.json")
if err != nil {
assert.Fail(t, "Reading the test file cve_metadata.json failed!", err)
}
byteArr2, err := readTestFile(currentFile, "cve_metadata2.json")
if err != nil {
assert.Fail(t, "Reading the test file cve_metadata2.json failed!", err)
}

testData := map[string][]byte{
"3.0": byteArr1,
"1.0": byteArr2,
}

expectedCVEfilesHashes := map[string]string{
"3.0": "b4a1877bbf70e861d3868fe65282b7183f6f4ce29aea057a0b8ac14ff5e1b9c5",
"1.0": "193e3a5fc5d320d5f2fef3bb86445b705253c9cc217cd62e0381a7c46700418a",
}
receivedCVEfilesHashes := calculateNewHashes(testData)
if !reflect.DeepEqual(expectedCVEfilesHashes, receivedCVEfilesHashes) {
assert.Fail(t, "The caculateNewCVEfilesHashes function doesn't work as expected!", "Want: %v \n Have: %v \n",
expectedCVEfilesHashes, receivedCVEfilesHashes)
}
}

func TestExtractOldCVEfilesHashes(t *testing.T) {
hashesInput := []string{
"1.0:asf213dwadadafrsfaewdsdqwdfescc;2.1:dwadakdekkdmsmmmmmswqw;3.0:ldwadladlcdkwjqsjrgtj;",
"",
"1.0:asf213dwadadafrsfaewdsdqwdfescc;",
}
expectedOutput := []map[string]string{
{
"1.0": "asf213dwadadafrsfaewdsdqwdfescc",
"2.1": "dwadakdekkdmsmmmmmswqw",
"3.0": "ldwadladlcdkwjqsjrgtj",
},
nil,
{
"1.0": "asf213dwadadafrsfaewdsdqwdfescc",
},
}

for i, input := range hashesInput {
received := extractOldHashes(input)

if !reflect.DeepEqual(expectedOutput[i], received) {
assert.Fail(t, "The responce doesn't contain an expected element!", "Want: %v \nHave: %v \n",
expectedOutput[i], received)
}
}
}

func TestCreateNewUpdaterFlag(t *testing.T) {
commonOldVersionHashMap := map[string]string{
"1.0": "asf213d",
"2.0": "dwadakd",
"3.0": "ldwadla",
}
inputsOldHashes := []map[string]string{
nil,
commonOldVersionHashMap,
commonOldVersionHashMap,
commonOldVersionHashMap,
commonOldVersionHashMap,
commonOldVersionHashMap,
commonOldVersionHashMap,
}
inputNewHashes := []map[string]string{
nil,
nil,
{
"3.0": "wprsjk",
},
{
"0.1": "wwwooo",
},
{
"2.1": "kkkkkk",
},
{
"4.0": "mamwms",
},
{
"0.1": "wwwooo",
"2.1": "kkkkkk",
"3.0": "wprsjk",
"4.0": "mamwms",
},
}

inputVersionsToBeUpdated := [][]string{
nil,
nil,
{
"3.0",
},
{
"0.1",
},
{
"2.1",
},
{
"4.0",
},
{
"0.1",
"2.1",
"3.0",
"4.0",
},
}

expectedOutPut := []string{
"",
"1.0:asf213d;2.0:dwadakd;3.0:ldwadla;",
"1.0:asf213d;2.0:dwadakd;3.0:wprsjk;",
"0.1:wwwooo;1.0:asf213d;2.0:dwadakd;3.0:ldwadla;",
"1.0:asf213d;2.0:dwadakd;2.1:kkkkkk;3.0:ldwadla;",
"1.0:asf213d;2.0:dwadakd;3.0:ldwadla;4.0:mamwms;",
"0.1:wwwooo;1.0:asf213d;2.0:dwadakd;2.1:kkkkkk;3.0:wprsjk;4.0:mamwms;",
}

for i, oldMap := range inputsOldHashes {
received := createNewUpdaterFlag(oldMap, inputNewHashes[i], inputVersionsToBeUpdated[i])

if received != expectedOutPut[i] {
assert.Fail(t, "The responce is not the expected updaterFlag!", "Want: %v \nHave: %v \n",
expectedOutPut[i], received)
}
}
}
48 changes: 48 additions & 0 deletions ext/vulnsrc/photon/parse_utilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package photon

import (
"encoding/json"
"io"
"io/ioutil"

"github.com/quay/clair/v3/pkg/commonerr"
log "github.com/sirupsen/logrus"
)

type versionsInfo struct {
Versions []string `json:"branches"`
}

// parseCVEinfoJSON parses the json information into
// slice of cve-s
func parseCVEinfoJSON(jsonReader io.Reader) ([]cve, error) {
body, err := ioutil.ReadAll(jsonReader)
if err != nil {
log.Fatal(err)
}

var vulnerabilities []cve
err = json.Unmarshal(body, &vulnerabilities)
if err != nil {
log.WithError(err).Error("Error unmarshaling!")
return nil, commonerr.ErrCouldNotParse
}
return vulnerabilities, nil
}

// parseVersions parses the versions information into
// slice of string
func parseVersions(jsonReader io.Reader) ([]string, error) {
body, err := ioutil.ReadAll(jsonReader)
if err != nil {
log.Fatal(err)
}

var photonVersions versionsInfo
err = json.Unmarshal(body, &photonVersions)
if err != nil {
log.WithError(err).Error("Error unmarshaling!")
return nil, commonerr.ErrCouldNotParse
}
return photonVersions.Versions, nil
}
Loading