diff --git a/Dockerfile-static b/Dockerfile-static new file mode 100644 index 0000000..5047cf6 --- /dev/null +++ b/Dockerfile-static @@ -0,0 +1,24 @@ +FROM golang:1.19.4-alpine + +RUN apk add --upgrade gcc musl-dev pkgconfig g++ make git protoc cmake boost-dev +RUN apk add hyperscan-dev --repository=https://dl-cdn.alpinelinux.org/alpine/v3.13/community +RUN apk add ragel + +ENV PKG_CONFIG_PATH=/usr/local/include/hs/ \ + CGO_CFLAGS="-I/usr/local/include/hyperscan/src" \ + LD_LIBRARY_PATH=/usr/local/lib:/usr/local/include/hs/lib:$LD_LIBRARY_PATH + +RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.1 \ + && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0 + +WORKDIR /go/src +RUN git clone https://github.com/intel/hyperscan.git +RUN cd hyperscan && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=MinSizeRel + +WORKDIR /go/src/SecretScanner +ENV PKG_CONFIG_PATH=/go/src/hyperscan/build +ENV CGO_LDFLAGS="-L /go/src/hyperscan/build/lib -static" +ENV CGO_CFLAGS="-I/go/src/hyperscan/src" +ENV GO_BUILD_EXTRA="-buildvcs=false" + +ENTRYPOINT ["/bin/sh", "-c", "make clean && make"] diff --git a/Makefile b/Makefile index 21acc41..62b0df9 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,10 @@ clean: -rm ./SecretScanner SecretScanner: $(PWD)/**/*.go $(PWD)/agent-plugins-grpc/proto/*.go - go build -buildvcs=false -v . + go build $(GO_BUILD_EXTRA) -.PHONY: clean +static: + docker build -t static-secret-scanner -f Dockerfile-static . + docker run -v $(PWD):/go/src/SecretScanner static-secret-scanner + +.PHONY: clean static diff --git a/README.md b/README.md index bc9cd58..83d9c43 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,21 @@ docker pull node:8.11 docker run -it --rm --name=deepfence-secretscanner -v $(pwd):/home/deepfence/output -v /var/run/docker.sock:/var/run/docker.sock deepfenceio/deepfence_secret_scanner:latest -image-name node:8.11 ``` +# CLI + +Download the binary and yaml file. The binary can run to perform secret scans: +Scan a local directory +```shell +./SecretScanner -local -quiet=false -config-path +``` +Scan a local docker image +```shell +./SecretScanner -quiet=false -image-name -config-path +``` +Scan the filesystem of a running container using id or name +```shell +./SecretScanner -quiet=false -config-path -container-id +``` # Credits We have built upon the configuration file from [shhgit](https://github.com/eth0izzle/shhgit) project. diff --git a/agent-plugins-grpc b/agent-plugins-grpc index 6584ed0..6475832 160000 --- a/agent-plugins-grpc +++ b/agent-plugins-grpc @@ -1 +1 @@ -Subproject commit 6584ed03e8ccf98af554015a2363bb627a23f84c +Subproject commit 6475832e96845428231a3b047b80ac79678d76a7 diff --git a/core/options.go b/core/options.go index 5d86d37..f41b471 100644 --- a/core/options.go +++ b/core/options.go @@ -66,7 +66,7 @@ func ParseOptions() (*Options, error) { MaxSecrets: flag.Uint("max-secrets", 1000, "Maximum number of secrets to find in one container image or file system."), ContainerId: flag.String("container-id", "", "Id of existing container ID"), ContainerNS: flag.String("container-ns", "", "Namespace of existing container to scan, empty for docker runtime"), - Quiet: flag.Bool("quiet", false, "Don't display any output in stdout"), + Quiet: flag.Bool("quiet", true, "Don't display any output in stdout"), } flag.Var(options.ConfigPath, "config-path", "Searches for config.yaml from given directory. If not set, tries to find it from SecretScanner binary's and current directory. Can be specified multiple times.") flag.Parse() diff --git a/core/util.go b/core/util.go index b0cbabe..d0f8297 100644 --- a/core/util.go +++ b/core/util.go @@ -65,7 +65,7 @@ func GetJsonFilepath(input string) (string, error) { } } if JsonFilename == "" { - JsonFilename = getSanitizedString(input) + "-secrets.json" + JsonFilename = getSanitizedString(input) + "secrets.json" } jsonFilePath := filepath.Join(outputDir, JsonFilename) GetSession().Log.Info("Complete json file path and name: %s", jsonFilePath) diff --git a/main.go b/main.go index 48a9753..92e1b28 100644 --- a/main.go +++ b/main.go @@ -57,15 +57,18 @@ var session = core.GetSession() // Error, if any. Otherwise, returns nil func findSecretsInImage(image string) (*output.JsonImageSecretsOutput, error) { + jsonImageSecretsOutput := output.JsonImageSecretsOutput{ImageName: image} + jsonImageSecretsOutput.SetTime() + if !(*session.Options.Quiet) { + jsonImageSecretsOutput.PrintJsonHeader() + } res, err := scan.ExtractAndScanImage(image) if err != nil { return nil, err } - jsonImageSecretsOutput := output.JsonImageSecretsOutput{ImageName: image} - jsonImageSecretsOutput.SetTime() - jsonImageSecretsOutput.SetImageId(res.ImageId) - jsonImageSecretsOutput.PrintJsonHeader() - jsonImageSecretsOutput.PrintJsonFooter() + if !(*session.Options.Quiet) { + jsonImageSecretsOutput.PrintJsonFooter() + } jsonImageSecretsOutput.SetSecrets(res.Secrets) return &jsonImageSecretsOutput, nil @@ -79,17 +82,20 @@ func findSecretsInImage(image string) (*output.JsonImageSecretsOutput, error) { func findSecretsInDir(dir string) (*output.JsonDirSecretsOutput, error) { var isFirstSecret bool = true var numSecrets uint = 0 + jsonDirSecretsOutput := output.JsonDirSecretsOutput{DirName: *session.Options.Local} + jsonDirSecretsOutput.SetTime() + if !(*session.Options.Quiet) { + jsonDirSecretsOutput.PrintJsonHeader() + } secrets, err := scan.ScanSecretsInDir("", "", dir, &isFirstSecret, &numSecrets, nil) if err != nil { core.GetSession().Log.Error("findSecretsInDir: %s", err) return nil, err } - - jsonDirSecretsOutput := output.JsonDirSecretsOutput{DirName: *session.Options.Local} - jsonDirSecretsOutput.SetTime() - jsonDirSecretsOutput.PrintJsonHeader() - jsonDirSecretsOutput.PrintJsonFooter() + if !(*session.Options.Quiet) { + jsonDirSecretsOutput.PrintJsonFooter() + } jsonDirSecretsOutput.SetSecrets(secrets) return &jsonDirSecretsOutput, nil @@ -102,15 +108,18 @@ func findSecretsInDir(dir string) (*output.JsonDirSecretsOutput, error) { // Error, if any. Otherwise, returns nil func findSecretsInContainer(containerId string, containerNS string) (*output.JsonImageSecretsOutput, error) { + jsonImageSecretsOutput := output.JsonImageSecretsOutput{ContainerId: containerId} + jsonImageSecretsOutput.SetTime() + if !(*session.Options.Quiet) { + jsonImageSecretsOutput.PrintJsonHeader() + } res, err := scan.ExtractAndScanContainer(containerId, containerNS) if err != nil { return nil, err } - jsonImageSecretsOutput := output.JsonImageSecretsOutput{ContainerId: containerId} - jsonImageSecretsOutput.SetTime() - jsonImageSecretsOutput.SetImageId(res.ContainerId) - jsonImageSecretsOutput.PrintJsonHeader() - jsonImageSecretsOutput.PrintJsonFooter() + if !(*session.Options.Quiet) { + jsonImageSecretsOutput.PrintJsonFooter() + } jsonImageSecretsOutput.SetSecrets(res.Secrets) return &jsonImageSecretsOutput, nil diff --git a/output/output.go b/output/output.go index 4b630af..162ae89 100644 --- a/output/output.go +++ b/output/output.go @@ -43,11 +43,11 @@ type JsonDirSecretsOutput struct { } type JsonImageSecretsOutput struct { - Timestamp time.Time - ImageName string `json:"Image Name"` - ImageId string `json:"Image ID"` + Timestamp time.Time + ImageName string `json:"Image Name"` + ImageId string `json:"Image ID"` ContainerId string `json:"Container ID"` - Secrets []SecretFound + Secrets []SecretFound } func (imageOutput *JsonImageSecretsOutput) SetImageName(imageName string) { @@ -111,6 +111,7 @@ func (imageOutput JsonImageSecretsOutput) PrintJsonHeader() { fmt.Printf(Indent+"\"Timestamp\": \"%s\",\n", time.Now().Format("2006-01-02 15:04:05.000000000 -07:00")) fmt.Printf(Indent+"\"Image Name\": \"%s\",\n", imageOutput.ImageName) fmt.Printf(Indent+"\"Image ID\": \"%s\",\n", imageOutput.ImageId) + fmt.Printf(Indent+"\"Container ID\": \"%s\",\n", imageOutput.ContainerId) fmt.Printf(Indent + "\"Secrets\": [\n") } diff --git a/scan/process_image.go b/scan/process_image.go index a26b9a7..c16d5a9 100644 --- a/scan/process_image.go +++ b/scan/process_image.go @@ -136,7 +136,7 @@ func ScanSecretsInDir(layer string, baseDir string, fullDir string, isFirstSecre var scanDirPath string if layer != "" { - scanDirPath = strings.TrimPrefix(path, baseDir + "/" + layer) + scanDirPath = strings.TrimPrefix(path, baseDir+"/"+layer) if scanDirPath == "" { scanDirPath = "/" } @@ -190,14 +190,14 @@ func ScanSecretsInDir(layer string, baseDir string, fullDir string, isFirstSecre session.Log.Error("scanSecretsInDir: %s", err) // return tempSecretsFound, err } - if *session.Options.Quiet { + if !(*session.Options.Quiet) { output.PrintColoredSecrets(secrets, isFirstSecret) } tempSecretsFound = append(tempSecretsFound, secrets...) } secrets = signature.MatchSimpleSignatures(relPath, file.Filename, file.Extension, layer, numSecrets) - if *session.Options.Quiet { + if !(*session.Options.Quiet) { output.PrintColoredSecrets(secrets, isFirstSecret) } tempSecretsFound = append(tempSecretsFound, secrets...) @@ -378,7 +378,7 @@ func untar(tarName string, xpath string) (err error) { relPath := strings.Split(fileName, "/") var absDirPath string if len(relPath) > 1 { - dirs := relPath[0: len(relPath) - 1] + dirs := relPath[0 : len(relPath)-1] absDirPath = filepath.Join(absPath, strings.Join(dirs, "/")) } if err := os.MkdirAll(absDirPath, 0755); err != nil { @@ -402,7 +402,7 @@ func untar(tarName string, xpath string) (err error) { // fmt.Printf("x %s\n", absFileName) n, cpErr := io.Copy(file, tr) if closeErr := file.Close(); closeErr != nil { // close file immediately - fmt.Println("clserr:"+closeErr.Error()) + fmt.Println("clserr:" + closeErr.Error()) return err } if cpErr != nil {