Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ jobs:
- name: Check if registry-library build is working
run: cd registry-library && bash ./build.sh

- name: Run Gosec Security Scanner
run: |
export PATH=$PATH:$(go env GOPATH)/bin
go install github.com/securego/gosec/v2/cmd/gosec@latest
./run_gosec.sh
if [[ $? != 0 ]]
then
echo "gosec scanner failed to run "
exit 1
fi

- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
# Path to SARIF file relative to the root of the repository
sarif_file: gosec.sarif


docker:
name: Check docker builds
runs-on: ubuntu-latest
Expand Down Expand Up @@ -63,6 +81,7 @@ jobs:
- name: Upload coverage to Codecov
uses: codecov/[email protected]


test_minikube:
name: Test Devfile Registry
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions index/generator/library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func CreateIndexFile(index []schema.Schema, indexFilePath string) error {
return fmt.Errorf("failed to marshal %s data: %v", indexFilePath, err)
}

/* #nosec G306 -- index file does not contain any sensitive data*/
err = ioutil.WriteFile(indexFilePath, bytes, 0644)
if err != nil {
return fmt.Errorf("failed to write %s: %v", indexFilePath, err)
Expand Down Expand Up @@ -312,6 +313,7 @@ func parseStackDevfile(devfileDirPath string, stackName string, force bool, vers
}
}

/* #nosec G304 -- devfilePath is produced using filepath.Join which cleans the input path */
bytes, err := ioutil.ReadFile(devfilePath)
if err != nil {
return fmt.Errorf("failed to read %s: %v", devfilePath, err)
Expand Down Expand Up @@ -403,6 +405,7 @@ func parseStackDevfile(devfileDirPath string, stackName string, force bool, vers
func parseExtraDevfileEntries(registryDirPath string, force bool) ([]schema.Schema, error) {
var index []schema.Schema
extraDevfileEntriesPath := path.Join(registryDirPath, extraDevfileEntries)
/* #nosec G304 -- extraDevfileEntriesPath is produced using path.Join which cleans the input path */
bytes, err := ioutil.ReadFile(extraDevfileEntriesPath)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %v", extraDevfileEntriesPath, err)
Expand Down Expand Up @@ -487,6 +490,7 @@ func parseExtraDevfileEntries(registryDirPath string, force bool) ([]schema.Sche
return index, nil
}

/* #nosec G304 -- stackYamlPath is produced from file.Join which cleans the input path */
func parseStackInfo(stackYamlPath string) (schema.Schema, error) {
var index schema.Schema
bytes, err := ioutil.ReadFile(stackYamlPath)
Expand Down
13 changes: 8 additions & 5 deletions index/generator/library/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,27 @@ func CloneRemoteStack(git *schema.Git, path string, verbose bool) (err error) {
// returns byte array of zip file and error if occurs otherwise is nil. If git.SubDir is set, then
// zip file will contain contents of the specified subdirectory instead of the whole downloaded git repo.
func DownloadStackFromGit(git *schema.Git, path string, verbose bool) ([]byte, error) {
zipPath := fmt.Sprintf("%s.zip", path)
cleanPath := filepath.Clean(path)
zipPath := fmt.Sprintf("%s.zip", cleanPath)

// Download from given git url. Downloaded result contains subDir
// when specified, if error return empty bytes.
if err := CloneRemoteStack(git, path, verbose); err != nil {
if err := CloneRemoteStack(git, cleanPath, verbose); err != nil {
return []byte{}, err
}

// Throw error if path was not created
if _, err := os.Stat(path); os.IsNotExist(err) {
if _, err := os.Stat(cleanPath); os.IsNotExist(err) {
return []byte{}, err
}

// Zip directory containing downloaded git repo
if err := ZipDir(path, zipPath); err != nil {
if err := ZipDir(cleanPath, zipPath); err != nil {
return []byte{}, err
}

// Read bytes from response and return, error will be nil if successful
/* #nosec G304 -- zipPath is constructed from a clean path */
return ioutil.ReadFile(zipPath)
}

Expand All @@ -152,7 +154,7 @@ func DownloadStackFromZipUrl(zipUrl string, subDir string, path string) ([]byte,

// downloadStackFromZipUrl downloads the zip file containing the stack at a given url
func downloadStackFromZipUrl(zipUrl string, subDir string, path string, fs filesystem.Filesystem) ([]byte, error) {
zipDst := fmt.Sprintf("%s.zip", path)
zipDst := fmt.Sprintf("%s.zip", filepath.Clean(path))

// Create path if does not exist
if err := fs.MkdirAll(path, os.ModePerm); err != nil {
Expand Down Expand Up @@ -192,6 +194,7 @@ func downloadStackFromZipUrl(zipUrl string, subDir string, path string, fs files
}

// Read bytes from response and return, error will be nil if successful
/* #nosec G304 -- zipDest is produced using a cleaned path */
return ioutil.ReadFile(zipDst)
}

Expand Down
4 changes: 3 additions & 1 deletion index/server/pkg/server/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/url"
"os"
"path"
"path/filepath"
"regexp"

"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
Expand Down Expand Up @@ -275,7 +276,7 @@ func serveDevfileStarterProjectWithVersion(c *gin.Context) {
localLoc = downloadFilePath
}

downloadBytes, err = ioutil.ReadFile(localLoc)
downloadBytes, err = ioutil.ReadFile(filepath.Clean(localLoc))
if err != nil {
log.Print(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
Expand Down Expand Up @@ -556,6 +557,7 @@ func fetchDevfile(c *gin.Context, name string, version string) ([]byte, indexSch
}
if sampleDevfilePath != "" {
if _, err = os.Stat(sampleDevfilePath); err == nil {
/* #nosec G304 -- sampleDevfilePath is constructed from path.Join which cleans the input paths */
bytes, err = ioutil.ReadFile(sampleDevfilePath)
}
if err != nil {
Expand Down
13 changes: 11 additions & 2 deletions index/server/pkg/server/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,18 @@ var getIndexLatency = prometheus.NewHistogramVec(
func ServeRegistry() {
// Enable metrics
// Run on a separate port and router from the index server so that it's not exposed publicly
http.Handle("/metrics", promhttp.Handler())

handler := http.NewServeMux()
handler.Handle("/metrics", promhttp.Handler())
prometheus.MustRegister(getIndexLatency)
go http.ListenAndServe(":7071", nil)
indexServer := &http.Server{
Addr: ":7071",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}

go indexServer.ListenAndServe()

// Wait until registry is up and running
err := wait.PollImmediate(time.Millisecond, time.Second*30, func() (bool, error) {
Expand Down
1 change: 1 addition & 0 deletions index/server/pkg/server/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func pushStackToRegistry(versionComponent indexSchema.Version, stackName string)
if _, err := os.Stat(resourcePath); os.IsNotExist(err) {
resourcePath = filepath.Join(stacksPath, stackName, resource)
}
/* #nosec G304 -- resourcePath is constructed from filepath.Join which cleans the input paths */
resourceContent, err := ioutil.ReadFile(resourcePath)
if err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions index/server/pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func IsHtmlRequested(acceptHeader []string) bool {
// EncodeIndexIconToBase64 encodes all index icons to base64 format given the index file path
func EncodeIndexIconToBase64(indexPath string, base64IndexPath string) ([]byte, error) {
// load index
/* #nosec G304 -- indexPath is derived from known paths set in the docker image */
bytes, err := ioutil.ReadFile(indexPath)
if err != nil {
return nil, err
Expand Down Expand Up @@ -86,6 +87,7 @@ func encodeToBase64(uri string) (string, error) {
// load the content from the given uri
var bytes []byte
if url.Scheme == "http" || url.Scheme == "https" {
/* #nosec G107 -- uri is taken from the index file. Stacks with URLs to a devile icon should be vetted beforehand */
resp, err := http.Get(uri)
if err != nil {
return "", err
Expand All @@ -97,6 +99,7 @@ func encodeToBase64(uri string) (string, error) {
return "", err
}
} else {
/* #nosec G304 -- uri is derived from known paths set in the docker image */
bytes, err = ioutil.ReadFile(uri)
if err != nil {
return "", err
Expand All @@ -121,6 +124,7 @@ func encodeToBase64(uri string) (string, error) {
// ReadIndexPath reads the index from the path and unmarshalls it into the index
func ReadIndexPath(indexPath string) ([]indexSchema.Schema, error) {
// load index
/* #nosec G304 -- not user input */
bytes, err := ioutil.ReadFile(indexPath)
if err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions registry-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.14
require (
github.com/containerd/containerd v1.5.9
github.com/devfile/registry-support/index/generator v0.0.0-20220624203950-e7282a4695b6
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.4.0
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v1.2.1
Expand Down
9 changes: 3 additions & 6 deletions registry-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/devfile/api/v2 v2.0.0-20211021164004-dabee4e633ed/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/api/v2 v2.0.0-20220117162434-6e6e6a8bc14c/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/library v1.2.1-0.20211104222135-49d635cb492f/go.mod h1:uFZZdTuRqA68FVe/JoJHP92CgINyQkyWnM2Qyiim+50=
github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de/go.mod h1:GSPfJaBg0+bBjBHbwBE5aerJLH6tWGQu2q2rHYd9czM=
github.com/devfile/registry-support/index/generator v0.0.0-20220222194908-7a90a4214f3e h1:3WAjUoyAmCBzUpx+sO0dSgkH74uSW1y886wGbojD2D8=
github.com/devfile/registry-support/index/generator v0.0.0-20220222194908-7a90a4214f3e/go.mod h1:iRPBxs+ZjfLEduVXpCCIOzdD2588Zv9OCs/CcXMcCCY=
github.com/devfile/registry-support/index/generator v0.0.0-20220624203950-e7282a4695b6 h1:bTbZxKSjF9xfiUuOKpoyX7P/ZcnIRy993+JBvkQ91hw=
github.com/devfile/registry-support/index/generator v0.0.0-20220624203950-e7282a4695b6/go.mod h1:1fyDJL+fPHtcrYA6yjSVWeLmXmjCNth0d5Rq1rvtryc=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
Expand Down Expand Up @@ -511,13 +507,15 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
Expand Down Expand Up @@ -660,7 +658,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down Expand Up @@ -1348,8 +1345,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
Expand Down
43 changes: 32 additions & 11 deletions registry-library/library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"archive/zip"
"encoding/json"
"fmt"
"github.com/hashicorp/go-multierror"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -233,7 +234,8 @@ func PrintRegistry(registryURLs string, devfileType string, options RegistryOpti
}
}
}
w.Flush()

_ = w.Flush()
return nil
}

Expand Down Expand Up @@ -314,17 +316,20 @@ func DownloadStarterProjectAsDir(path string, registryURL string, stack string,
defer archive.Close()

// Extract files from starter project archive to specified directory path
cleanPath := filepath.Clean(path)
for _, file := range archive.File {
filePath := filepath.Join(path, file.Name)
filePath := filepath.Join(cleanPath, filepath.Clean(file.Name))

// validate extracted filepath
if filePath != file.Name && !strings.HasPrefix(filePath, filepath.Clean(path)+string(os.PathSeparator)) {
if filePath != file.Name && !strings.HasPrefix(filePath, cleanPath+string(os.PathSeparator)) {
return fmt.Errorf("invalid file path %s", filePath)
}

// if file is a directory, create it in destination and continue to next file
if file.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
if err = os.MkdirAll(filePath, os.ModePerm); err != nil {
return fmt.Errorf("error creating directory %s: %v", filepath.Dir(filePath), err)
}
continue
}

Expand All @@ -334,6 +339,7 @@ func DownloadStarterProjectAsDir(path string, registryURL string, stack string,
}

// open destination file
/* #nosec G304 -- filePath is produced using path.Join which cleans the dir path */
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return fmt.Errorf("error opening destination file at %s: %v", filePath, err)
Expand All @@ -346,12 +352,19 @@ func DownloadStarterProjectAsDir(path string, registryURL string, stack string,
}

// extract source file to destination file
/* #nosec G110 -- starter projects are vetted before they are added to a registry. Their contents can be seen before they are downloaded */
if _, err = io.Copy(dstFile, srcFile); err != nil {
return fmt.Errorf("error extracting file %s from archive %s to destination at %s: %v", file.Name, archivePath, filePath, err)
}

dstFile.Close()
srcFile.Close()
err = dstFile.Close()
if err != nil {
return err
}
err = srcFile.Close()
if err != nil {
return err
}
}

return nil
Expand All @@ -360,37 +373,45 @@ func DownloadStarterProjectAsDir(path string, registryURL string, stack string,
// DownloadStarterProject downloads a specified starter project archive to a given path
func DownloadStarterProject(path string, registryURL string, stack string, starterProject string, options RegistryOptions) error {
var fileStream *os.File
var returnedErr error

cleanPath := filepath.Clean(path)
// Download Starter Project archive bytes
bytes, err := DownloadStarterProjectAsBytes(registryURL, stack, starterProject, options)
if err != nil {
return err
}

// Error if parent directory does not exist
if _, err = os.Stat(filepath.Dir(path)); os.IsNotExist(err) {
if _, err = os.Stat(filepath.Dir(cleanPath)); os.IsNotExist(err) {
return fmt.Errorf("parent directory '%s' does not exist: %v", filepath.Dir(path), err)
}

// If file does not exist, create a new one
// Else open existing for overwriting
if _, err = os.Stat(path); os.IsNotExist(err) {
fileStream, err = os.Create(path)
fileStream, err = os.Create(cleanPath)
if err != nil {
return fmt.Errorf("failed to create file '%s': %v", path, err)
}
} else {
fileStream, err = os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, os.ModePerm)
fileStream, err = os.OpenFile(cleanPath, os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to open file '%s': %v", path, err)
}
}
defer fileStream.Close()

defer func() {
if err = fileStream.Close(); err != nil {
returnedErr = multierror.Append(returnedErr, err)
}
}()

// Write downloaded bytes to file
_, err = fileStream.Write(bytes)
if err != nil {
return fmt.Errorf("failed writing to '%s': %v", path, err)
returnedErr = multierror.Append(returnedErr, fmt.Errorf("failed writing to '%s': %v", path, err))
return returnedErr
}

return nil
Expand Down
Loading