diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80232ef3..0d8932fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,7 +92,8 @@ jobs: distribution: 'temurin' - name: Install Debricked CLI - run: go install -ldflags "-X main.version=${GITHUB_REF#refs/heads/}" ./cmd/debricked + run: | + go install -ldflags "-X main.version=${{ secrets.DEBRICKED_VERSION }}" ./cmd/debricked - name: Callgraph E2E run: ./scripts/test_e2e_callgraph_java_version.sh ${{matrix.java}} diff --git a/internal/callgraph/cgexec/command.go b/internal/callgraph/cgexec/command.go index aebff6c4..630c4c8e 100644 --- a/internal/callgraph/cgexec/command.go +++ b/internal/callgraph/cgexec/command.go @@ -109,7 +109,13 @@ func RunCommand(cmd ICommand, ctx IContext) error { go func() { err := cmd.Wait() if err != nil { - err = fmt.Errorf("Command '%s' executed in folder '%s' gave the following error: \n%s\n%s", args, cmd.GetDir(), cmd.GetStdOut().String(), cmd.GetStdErr().String()) + err = fmt.Errorf( + "Command '%s' executed in folder '%s' gave the following error: \n%s\n%s", + args, + cmd.GetDir(), + cmd.GetStdOut().String(), + cmd.GetStdErr().String(), + ) } done <- err diff --git a/internal/callgraph/config/config.go b/internal/callgraph/config/config.go index bec4c743..42db413d 100644 --- a/internal/callgraph/config/config.go +++ b/internal/callgraph/config/config.go @@ -6,6 +6,7 @@ type IConfig interface { Kwargs() map[string]string Build() bool PackageManager() string + Version() string } type Config struct { @@ -14,15 +15,24 @@ type Config struct { kwargs map[string]string build bool packageManager string + version string } -func NewConfig(language string, args []string, kwargs map[string]string, build bool, packageManager string) Config { +func NewConfig( + language string, + args []string, + kwargs map[string]string, + build bool, + packageManager string, + version string, +) Config { return Config{ language, args, kwargs, build, packageManager, + version, } } @@ -45,3 +55,7 @@ func (c Config) Build() bool { func (c Config) PackageManager() string { return c.packageManager } + +func (c Config) Version() string { + return c.version +} diff --git a/internal/callgraph/generator.go b/internal/callgraph/generator.go index 55c71a9c..4db68086 100644 --- a/internal/callgraph/generator.go +++ b/internal/callgraph/generator.go @@ -16,6 +16,7 @@ type DebrickedOptions struct { Inclusions []string Configs []config.IConfig Timeout int + Version string } type IGenerator interface { diff --git a/internal/callgraph/generator_test.go b/internal/callgraph/generator_test.go index b764d4a0..1ed55b61 100644 --- a/internal/callgraph/generator_test.go +++ b/internal/callgraph/generator_test.go @@ -30,7 +30,7 @@ func TestGenerate(t *testing.T) { ) configs := []config.IConfig{ - config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven"), + config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven", ""), } ctx, _ := ctxTestdata.NewContextMock() err := g.Generate( @@ -51,7 +51,7 @@ func TestGenerateWithTimer(t *testing.T) { ) configs := []config.IConfig{ - config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven"), + config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven", ""), } err := g.GenerateWithTimer( DebrickedOptions{ @@ -73,7 +73,7 @@ func TestGenerateInvokeError(t *testing.T) { ) configs := []config.IConfig{ - config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven"), + config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven", ""), } ctx, _ := ctxTestdata.NewContextMock() err := g.Generate( @@ -94,7 +94,7 @@ func TestGenerateScheduleError(t *testing.T) { ) configs := []config.IConfig{ - config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven"), + config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven", ""), } ctx, _ := ctxTestdata.NewContextMock() err := g.Generate( diff --git a/internal/callgraph/language/golang/job_test.go b/internal/callgraph/language/golang/job_test.go index d8ec0d21..576cb318 100644 --- a/internal/callgraph/language/golang/job_test.go +++ b/internal/callgraph/language/golang/job_test.go @@ -47,7 +47,7 @@ func TestRun(t *testing.T) { } }() - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() rootFileDir := filepath.Dir("testdata/fixture/app.go") @@ -65,7 +65,7 @@ func TestRun(t *testing.T) { func TestRunCallgraphMockError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() callgraphMock := testdata.CallgraphMock{RunCallGraphError: fmt.Errorf("error")} @@ -83,7 +83,7 @@ func TestRunCallgraphMockError(t *testing.T) { func TestRunPostProcessZipFileError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() archiveMock := ioTestData.ArchiveMock{ZipFileError: fmt.Errorf("error")} fs := io.FileSystem{} @@ -98,7 +98,7 @@ func TestRunPostProcessZipFileError(t *testing.T) { func TestRunPostProcessB64Error(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} @@ -114,7 +114,7 @@ func TestRunPostProcessB64Error(t *testing.T) { func TestRunPostProcessCleanupError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} @@ -130,7 +130,7 @@ func TestRunPostProcessCleanupError(t *testing.T) { func TestRunPostProcessCleanupNoFileExistError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} @@ -148,7 +148,7 @@ func TestRunPostProcessCleanupNoFileExistError(t *testing.T) { func TestRunWithErrorsIsNotExistFalse(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() fs := ioTestData.FileSystemMock{} @@ -170,7 +170,7 @@ func TestRunWithErrorsIsNotExistFalse(t *testing.T) { func TestRunWithErrorsIsNotExistTrue(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} - config := conf.NewConfig("golang", nil, nil, true, "go") + config := conf.NewConfig("golang", nil, nil, true, "go", "") ctx, _ := ctxTestdata.NewContextMock() fs := ioTestData.FileSystemMock{} diff --git a/internal/callgraph/language/golang/strategy_test.go b/internal/callgraph/language/golang/strategy_test.go index fa1e1ec3..d453b4ab 100644 --- a/internal/callgraph/language/golang/strategy_test.go +++ b/internal/callgraph/language/golang/strategy_test.go @@ -22,7 +22,7 @@ func TestNewStrategy(t *testing.T) { s = NewStrategy(nil, []string{"file-1", "file-2"}, []string{}, []string{}, nil, nil) assert.NotNil(t, s) - conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go") + conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go", "") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1"} finder.FindRootsNames = testFiles @@ -39,7 +39,7 @@ func TestInvokeNoFiles(t *testing.T) { } func TestInvokeOneFile(t *testing.T) { - conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go") + conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go", "") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1"} finder.FindRootsNames = testFiles @@ -51,7 +51,7 @@ func TestInvokeOneFile(t *testing.T) { } func TestInvokeManyFiles(t *testing.T) { - conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go") + conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go", "") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2"} finder.FindRootsNames = testFiles @@ -62,7 +62,7 @@ func TestInvokeManyFiles(t *testing.T) { } func TestInvokeWithErrors(t *testing.T) { - conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go") + conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go", "") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2"} finder.FindRootsNames = testFiles @@ -82,7 +82,7 @@ func TestInvokeWithErrors(t *testing.T) { } func TestInvokeNoRoots(t *testing.T) { - conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go") + conf := config.NewConfig("golang", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "go", "") finder := testdata.NewEmptyFinderMock() testFiles := []string{} finder.FindRootsNames = testFiles diff --git a/internal/callgraph/language/java11/README.md b/internal/callgraph/language/java/README.md similarity index 100% rename from internal/callgraph/language/java11/README.md rename to internal/callgraph/language/java/README.md diff --git a/internal/callgraph/language/java11/callgraph.go b/internal/callgraph/language/java/callgraph.go similarity index 55% rename from internal/callgraph/language/java11/callgraph.go rename to internal/callgraph/language/java/callgraph.go index ae99b525..6633dfd8 100644 --- a/internal/callgraph/language/java11/callgraph.go +++ b/internal/callgraph/language/java/callgraph.go @@ -1,16 +1,13 @@ package java import ( - "embed" - "path/filepath" + "fmt" + "regexp" "github.com/debricked/cli/internal/callgraph/cgexec" ioFs "github.com/debricked/cli/internal/io" ) -//go:embed embeded/SootWrapper.jar -var jarCallGraph embed.FS - type ICallgraph interface { RunCallGraphWithSetup() error RunCallGraph(callgraphJarPath string) error @@ -19,11 +16,13 @@ type ICallgraph interface { type Callgraph struct { cmdFactory ICmdFactory filesystem ioFs.IFileSystem + archive ioFs.IArchive workingDirectory string targetClasses []string targetDir string outputName string ctx cgexec.IContext + sootHandler ISootHandler } func NewCallgraph( @@ -33,7 +32,9 @@ func NewCallgraph( targetDir string, outputName string, filesystem ioFs.IFileSystem, + archive ioFs.IArchive, ctx cgexec.IContext, + sootHandler ISootHandler, ) Callgraph { return Callgraph{ cmdFactory: cmdFactory, @@ -42,43 +43,59 @@ func NewCallgraph( targetDir: targetDir, outputName: outputName, filesystem: filesystem, + archive: archive, ctx: ctx, + sootHandler: sootHandler, } } func (cg *Callgraph) RunCallGraphWithSetup() error { - jarFile, err := cg.filesystem.FsOpenEmbed(jarCallGraph, "embeded/SootWrapper.jar") + version, err := cg.javaVersion(".") if err != nil { return err } - defer cg.filesystem.FsCloseFile(jarFile) - tempDir, err := cg.filesystem.MkdirTemp("jar") + jarFile, err := cg.sootHandler.GetSootWrapper(version, cg.filesystem, cg.archive) if err != nil { + return err } - defer cg.filesystem.RemoveAll(tempDir) - tempJarFile := filepath.Join(tempDir, "SootWrapper.jar") - jarBytes, err := cg.filesystem.FsReadAll(jarFile) - if err != nil { + err = cg.RunCallGraph(jarFile) - return err - } + return err +} - err = cg.filesystem.FsWriteFile(tempJarFile, jarBytes, 0600) +func (cg *Callgraph) javaVersion(path string) (string, error) { + osCmd, err := cg.cmdFactory.MakeJavaVersionCmd(path, cg.ctx) if err != nil { - return err + return "", err } - err = cg.RunCallGraph(tempJarFile) - - return err + cmd := cgexec.NewCommand(osCmd) + err = cgexec.RunCommand(*cmd, cg.ctx) + if err != nil { + return "", err + } + javaVersionRegex := regexp.MustCompile(`\b(\d+)\.\d+\.\d+\b`) + match := javaVersionRegex.FindStringSubmatch(cmd.GetStdOut().String()) + if len(match) > 1 { + return match[1], nil + } else { + return "", fmt.Errorf("no version found in 'java --version' output, are you using a non-numeric version?") + } } func (cg *Callgraph) RunCallGraph(callgraphJarPath string) error { - osCmd, err := cg.cmdFactory.MakeCallGraphGenerationCmd(callgraphJarPath, cg.workingDirectory, cg.targetClasses, cg.targetDir, cg.outputName, cg.ctx) + osCmd, err := cg.cmdFactory.MakeCallGraphGenerationCmd( + callgraphJarPath, + cg.workingDirectory, + cg.targetClasses, + cg.targetDir, + cg.outputName, + cg.ctx, + ) if err != nil { return err diff --git a/internal/callgraph/language/java/callgraph_test.go b/internal/callgraph/language/java/callgraph_test.go new file mode 100644 index 00000000..94d3e5bf --- /dev/null +++ b/internal/callgraph/language/java/callgraph_test.go @@ -0,0 +1,61 @@ +package java + +import ( + "fmt" + "testing" + + "github.com/debricked/cli/internal/callgraph/language/java/testdata" + ioTestData "github.com/debricked/cli/internal/io/testdata" + "github.com/stretchr/testify/assert" +) + +func TestRunCallGraphWithSetupSootWrapperError(t *testing.T) { + + cmdMock := testdata.NewEchoCmdFactory() + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{} + shMock := testdata.MockSootHandler{GetSootWrapperError: fmt.Errorf("")} + cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, arcMock, nil, shMock) + + err := cg.RunCallGraphWithSetup() + + assert.Error(t, err) +} + +func TestRunCallGraphWithSetupSootVersionError(t *testing.T) { + + cmdMock := testdata.CmdFactoryMock{JavaVersionErr: fmt.Errorf("version error")} + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{} + shMock := testdata.MockSootHandler{} + cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, arcMock, nil, shMock) + + err := cg.RunCallGraphWithSetup() + + assert.Error(t, err) +} + +func TestRunCallGraphMock(t *testing.T) { + cmdMock := testdata.NewEchoCmdFactory() + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{} + shMock := testdata.MockSootHandler{} + cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, arcMock, nil, shMock) + + err := cg.RunCallGraph(".") + + assert.Nil(t, err) +} + +func TestRunCallGraphErrorMock(t *testing.T) { + cmdMock := testdata.NewEchoCmdFactory() + cmdMock.CallGraphGenErr = fmt.Errorf("error") + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{} + shMock := testdata.MockSootHandler{} + cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, arcMock, nil, shMock) + + err := cg.RunCallGraph(".") + + assert.NotNil(t, err) +} diff --git a/internal/callgraph/language/java11/cmd_factory.go b/internal/callgraph/language/java/cmd_factory.go similarity index 83% rename from internal/callgraph/language/java11/cmd_factory.go rename to internal/callgraph/language/java/cmd_factory.go index 49821076..a8ed4a38 100644 --- a/internal/callgraph/language/java11/cmd_factory.go +++ b/internal/callgraph/language/java/cmd_factory.go @@ -10,6 +10,7 @@ type ICmdFactory interface { MakeMvnCopyDependenciesCmd(workingDirectory string, targetDir string, ctx cgexec.IContext) (*exec.Cmd, error) MakeCallGraphGenerationCmd(callgraphJarPath string, workingDirectory string, targetClasses []string, dependencyClasses string, outputName string, ctx cgexec.IContext) (*exec.Cmd, error) MakeBuildMavenCmd(workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) + MakeJavaVersionCmd(workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) } type CmdFactory struct{} @@ -71,3 +72,13 @@ func (_ CmdFactory) MakeBuildMavenCmd(workingDirectory string, ctx cgexec.IConte return cgexec.MakeCommand(workingDirectory, path, args, ctx), err } + +func (_ CmdFactory) MakeJavaVersionCmd(workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) { + path, err := exec.LookPath("java") + args := []string{ + "java", + "--version", + } + + return cgexec.MakeCommand(workingDirectory, path, args, ctx), err +} diff --git a/internal/callgraph/language/java11/cmd_factory_test.go b/internal/callgraph/language/java/cmd_factory_test.go similarity index 83% rename from internal/callgraph/language/java11/cmd_factory_test.go rename to internal/callgraph/language/java/cmd_factory_test.go index 1d0a939d..89707d35 100644 --- a/internal/callgraph/language/java11/cmd_factory_test.go +++ b/internal/callgraph/language/java/cmd_factory_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/assert" ) +const jarPath = "jarpath" + func TestMakeMvnCopyDependenciesCmd(t *testing.T) { targetDir := "target" ctx, _ := ctxTestdata.NewContextMock() @@ -21,7 +23,6 @@ func TestMakeMvnCopyDependenciesCmd(t *testing.T) { } func TestMakeCallGraphGenerationCmd(t *testing.T) { - jarPath := "jarpath" targetClasses := []string{"targetclasses"} dependencyClasses := "dependencypath" outputName := ".outputName" @@ -40,7 +41,6 @@ func TestMakeCallGraphGenerationCmd(t *testing.T) { } func TestMakeBuildMavenCmd(t *testing.T) { - jarPath := "jarpath" ctx, _ := ctxTestdata.NewContextMock() cmd, err := CmdFactory{}.MakeBuildMavenCmd(jarPath, ctx) @@ -52,3 +52,14 @@ func TestMakeBuildMavenCmd(t *testing.T) { assert.Contains(t, args, "-q") assert.Contains(t, args, "-DskipTests") } + +func TestMakeJavaVersionCmd(t *testing.T) { + ctx, _ := ctxTestdata.NewContextMock() + cmd, err := CmdFactory{}.MakeJavaVersionCmd(jarPath, ctx) + + assert.NoError(t, err) + assert.NotNil(t, cmd) + args := cmd.Args + assert.Contains(t, args, "java") + assert.Contains(t, args, "--version") +} diff --git a/internal/callgraph/language/java11/embeded/SootWrapper.jar b/internal/callgraph/language/java/embedded/SootWrapper.jar similarity index 95% rename from internal/callgraph/language/java11/embeded/SootWrapper.jar rename to internal/callgraph/language/java/embedded/SootWrapper.jar index c1f167d0..da1c7edb 100644 Binary files a/internal/callgraph/language/java11/embeded/SootWrapper.jar and b/internal/callgraph/language/java/embedded/SootWrapper.jar differ diff --git a/internal/callgraph/language/java11/embeded/gradle-script.groovy b/internal/callgraph/language/java/embedded/gradle-script.groovy similarity index 100% rename from internal/callgraph/language/java11/embeded/gradle-script.groovy rename to internal/callgraph/language/java/embedded/gradle-script.groovy diff --git a/internal/callgraph/language/java11/job.go b/internal/callgraph/language/java/job.go similarity index 81% rename from internal/callgraph/language/java11/job.go rename to internal/callgraph/language/java/job.go index 78592aba..42bfa04f 100644 --- a/internal/callgraph/language/java11/job.go +++ b/internal/callgraph/language/java/job.go @@ -22,21 +22,33 @@ const ( type Job struct { job.BaseJob - cmdFactory ICmdFactory - config conf.IConfig - archive io.IArchive - ctx cgexec.IContext - fs ioFs.IFileSystem + cmdFactory ICmdFactory + config conf.IConfig + archive io.IArchive + ctx cgexec.IContext + fs ioFs.IFileSystem + sootHandler ISootHandler } -func NewJob(dir string, files []string, cmdFactory ICmdFactory, writer ioFs.IFileWriter, archive io.IArchive, config conf.IConfig, ctx cgexec.IContext, fs ioFs.IFileSystem) *Job { +func NewJob( + dir string, + files []string, + cmdFactory ICmdFactory, + writer ioFs.IFileWriter, + archive io.IArchive, + config conf.IConfig, + ctx cgexec.IContext, + fs ioFs.IFileSystem, + sootHandler ISootHandler, +) *Job { return &Job{ - BaseJob: job.NewBaseJob(dir, files), - cmdFactory: cmdFactory, - config: config, - archive: archive, - ctx: ctx, - fs: fs, + BaseJob: job.NewBaseJob(dir, files), + cmdFactory: cmdFactory, + config: config, + archive: archive, + ctx: ctx, + fs: fs, + sootHandler: sootHandler, } } @@ -78,7 +90,9 @@ func (j *Job) Run() { targetDir, outputName, j.fs, + j.archive, j.ctx, + j.sootHandler, ) j.SendStatus("generating call graph") j.runCallGraph(&callgraph) diff --git a/internal/callgraph/language/java11/job_test.go b/internal/callgraph/language/java/job_test.go similarity index 85% rename from internal/callgraph/language/java11/job_test.go rename to internal/callgraph/language/java/job_test.go index 3217f796..1d4ebcce 100644 --- a/internal/callgraph/language/java11/job_test.go +++ b/internal/callgraph/language/java/job_test.go @@ -10,7 +10,7 @@ import ( ctxTestdata "github.com/debricked/cli/internal/callgraph/cgexec/testdata" conf "github.com/debricked/cli/internal/callgraph/config" jobTestdata "github.com/debricked/cli/internal/callgraph/job/testdata" - "github.com/debricked/cli/internal/callgraph/language/java11/testdata" + "github.com/debricked/cli/internal/callgraph/language/java/testdata" io "github.com/debricked/cli/internal/io" ioTestData "github.com/debricked/cli/internal/io/testdata" "github.com/stretchr/testify/assert" @@ -34,8 +34,9 @@ func TestNewJob(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, writer, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, writer, archiveMock, config, ctx, fs, shMock) assert.Equal(t, []string{"file"}, j.GetFiles()) assert.Equal(t, "dir", j.GetDir()) assert.False(t, j.Errors().HasError()) @@ -52,10 +53,11 @@ func TestRunMakeMavenCopyDependenciesCmdErr(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.Run() @@ -67,7 +69,7 @@ func TestRunMakeMavenCopyDependenciesCmdErr(t *testing.T) { func TestRun(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fsMock := ioTestData.FileSystemMock{} @@ -75,12 +77,13 @@ func TestRun(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.Run() - + fmt.Println("TestRun") fmt.Println(j.Errors().GetAll()) assert.False(t, j.Errors().HasError()) } @@ -88,7 +91,7 @@ func TestRun(t *testing.T) { func TestRunCallgraphMock(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() callgraphMock := testdata.CallgraphMock{} @@ -97,8 +100,9 @@ func TestRunCallgraphMock(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) j.runCallGraph(callgraphMock) assert.False(t, j.Errors().HasError()) @@ -107,7 +111,7 @@ func TestRunCallgraphMock(t *testing.T) { func TestRunCallgraphMockError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() callgraphMock := testdata.CallgraphMock{RunCallGraphWithSetupError: fmt.Errorf("error")} @@ -116,8 +120,9 @@ func TestRunCallgraphMockError(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) j.runCallGraph(callgraphMock) assert.True(t, j.Errors().HasError()) @@ -126,7 +131,7 @@ func TestRunCallgraphMockError(t *testing.T) { func TestRunPostProcessMock(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fsMock := ioTestData.FileSystemMock{} @@ -134,8 +139,9 @@ func TestRunPostProcessMock(t *testing.T) { archiveMock := io.NewArchiveWithStructs(dir, fsMock, zip) fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -145,12 +151,13 @@ func TestRunPostProcessMock(t *testing.T) { func TestRunPostProcessZipFileError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() archiveMock := ioTestData.ArchiveMock{ZipFileError: fmt.Errorf("error")} fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -160,13 +167,14 @@ func TestRunPostProcessZipFileError(t *testing.T) { func TestRunPostProcessB64Error(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} archiveMock := ioTestData.ArchiveMock{B64Error: fmt.Errorf("error")} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -176,13 +184,14 @@ func TestRunPostProcessB64Error(t *testing.T) { func TestRunPostProcessCleanupError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} + shMock := testdata.MockSootHandler{} archiveMock := ioTestData.ArchiveMock{CleanupError: fmt.Errorf("error")} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -192,15 +201,16 @@ func TestRunPostProcessCleanupError(t *testing.T) { func TestRunPostProcessCleanupNoFileExistError(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} err := &os.PathError{} err.Err = syscall.ENOENT archiveMock := ioTestData.ArchiveMock{CleanupError: err} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -210,15 +220,16 @@ func TestRunPostProcessCleanupNoFileExistError(t *testing.T) { func TestRunPostProcessFromRoot(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := io.FileSystem{} err := &os.PathError{} err.Err = syscall.ENOENT archiveMock := ioTestData.ArchiveMock{PathError: err, Dir: "."} + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) go jobTestdata.WaitStatus(j) j.runPostProcess() @@ -231,7 +242,7 @@ func TestRunWithErrorsIsNotExistFalse(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := ioTestData.FileSystemMock{} @@ -239,8 +250,9 @@ func TestRunWithErrorsIsNotExistFalse(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fs, zip) fs.IsNotExistBool = false + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) j.Errors().Critical(fmt.Errorf("error")) go jobTestdata.WaitStatus(j) @@ -255,7 +267,7 @@ func TestRunWithErrorsIsNotExistTrue(t *testing.T) { fileWriterMock := &ioTestData.FileWriterMock{} cmdFactoryMock := testdata.NewEchoCmdFactory() - config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") + config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven", "") ctx, _ := ctxTestdata.NewContextMock() fs := ioTestData.FileSystemMock{} @@ -263,8 +275,9 @@ func TestRunWithErrorsIsNotExistTrue(t *testing.T) { archiveMock := io.NewArchiveWithStructs("dir", fs, zip) fs.IsNotExistBool = true + shMock := testdata.MockSootHandler{} - j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs, shMock) j.Errors().Critical(fmt.Errorf("error")) go jobTestdata.WaitStatus(j) diff --git a/internal/callgraph/language/java11/language.go b/internal/callgraph/language/java/language.go similarity index 100% rename from internal/callgraph/language/java11/language.go rename to internal/callgraph/language/java/language.go diff --git a/internal/callgraph/language/java11/language_test.go b/internal/callgraph/language/java/language_test.go similarity index 100% rename from internal/callgraph/language/java11/language_test.go rename to internal/callgraph/language/java/language_test.go diff --git a/internal/callgraph/language/java/soot-wrapper.jar b/internal/callgraph/language/java/soot-wrapper.jar new file mode 100644 index 00000000..57c5c17c Binary files /dev/null and b/internal/callgraph/language/java/soot-wrapper.jar differ diff --git a/internal/callgraph/language/java/soot_handler.go b/internal/callgraph/language/java/soot_handler.go new file mode 100644 index 00000000..c8e6ae43 --- /dev/null +++ b/internal/callgraph/language/java/soot_handler.go @@ -0,0 +1,143 @@ +package java + +import ( + "embed" + "fmt" + "net/http" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + ioFs "github.com/debricked/cli/internal/io" +) + +type ISootHandler interface { + GetSootWrapper(version string, fs ioFs.IFileSystem, arc ioFs.IArchive) (string, error) +} + +type SootHandler struct{ cliVersion string } + +//go:embed embedded/SootWrapper.jar +var jarCallGraph embed.FS + +func (sh SootHandler) initializeSootWrapper(fs ioFs.IFileSystem, tempDir string) (string, error) { + jarFile, err := fs.FsOpenEmbed(jarCallGraph, "embedded/SootWrapper.jar") + if err != nil { + return "", err + } + defer fs.FsCloseFile(jarFile) + + tempJarFile := filepath.Join(tempDir, "SootWrapper.jar") + + jarBytes, err := fs.FsReadAll(jarFile) + if err != nil { + + return "", err + } + + err = fs.FsWriteFile(tempJarFile, jarBytes, 0600) + if err != nil { + + return "", err + } + + return tempJarFile, nil +} + +func (sh SootHandler) downloadSootWrapper(arc ioFs.IArchive, fs ioFs.IFileSystem, path string, version string) error { + dir, err := fs.MkdirTemp(".tmp") + if err != nil { + + return err + } + + zipPath := dir + "/soot_wrapper.zip" + zipFile, err := fs.Create(zipPath) + if err != nil { + + return err + } + defer zipFile.Close() + + err = sh.downloadCompressedSootWrapper(fs, zipFile, version) + if err != nil { + + return err + } + + return arc.UnzipFile(zipPath, path) +} + +func (sh SootHandler) downloadCompressedSootWrapper(fs ioFs.IFileSystem, zipFile *os.File, version string) error { + fullURLFile := strings.Join([]string{ + "https://github.com/debricked/cli/releases/download/", + sh.cliVersion, + "/soot-wrapper-", + version, + ".zip", + }, "") + + client := http.Client{ + CheckRedirect: func(r *http.Request, via []*http.Request) error { + r.URL.Opaque = r.URL.Path + + return nil + }, + } + resp, err := client.Get(fullURLFile) + if err != nil { + + return err + } + defer resp.Body.Close() + + _, err = fs.Copy(zipFile, resp.Body) + + return err +} + +func (sh SootHandler) GetSootWrapper(version string, fs ioFs.IFileSystem, arc ioFs.IArchive) (string, error) { + versionInt, err := strconv.Atoi(version) + if err != nil { + return "", fmt.Errorf("could not convert version to int") + } + version, err = sh.getSootHandlerJavaVersion(versionInt) + if err != nil { + return "", err + } + debrickedDir := ".debricked" + if _, err := fs.Stat(debrickedDir); fs.IsNotExist(err) { + err := fs.Mkdir(debrickedDir, 0755) + if err != nil { + return "", err + } + } + path, err := filepath.Abs(path.Join(debrickedDir, "soot-wrapper.jar")) + if err != nil { + + return "", err + } + if _, err := fs.Stat(path); fs.IsNotExist(err) { + if version == "21" { + return sh.initializeSootWrapper(fs, debrickedDir) + } + + return path, sh.downloadSootWrapper(arc, fs, path, version) + } + + return path, nil +} + +func (sh SootHandler) getSootHandlerJavaVersion(version int) (string, error) { + if version >= 21 { + return "21", nil + } else if version >= 17 { + return "17", nil + } else if version >= 11 { + return "11", nil + } else { + return "", fmt.Errorf("lowest supported version for running callgraph generation is 11") + } +} diff --git a/internal/callgraph/language/java/soot_handler_test.go b/internal/callgraph/language/java/soot_handler_test.go new file mode 100644 index 00000000..3c7605a5 --- /dev/null +++ b/internal/callgraph/language/java/soot_handler_test.go @@ -0,0 +1,230 @@ +package java + +import ( + "fmt" + "testing" + + ioFs "github.com/debricked/cli/internal/io" + ioTestData "github.com/debricked/cli/internal/io/testdata" + + "github.com/stretchr/testify/assert" +) + +var sootHandler = SootHandler{} + +func TestInitializeSootWrapper(t *testing.T) { + fsMock := ioTestData.FileSystemMock{} + tempDir, err := fsMock.MkdirTemp(".tmp") + assert.NoError(t, err) + path, err := sootHandler.initializeSootWrapper(fsMock, tempDir) + assert.NotNil(t, path) + assert.NoError(t, err) +} + +func TestInitializeSootWrapperOpenEmbedError(t *testing.T) { + errString := "fs open embed error" + fsMock := ioTestData.FileSystemMock{FsOpenEmbedError: fmt.Errorf(errString)} //nolint + tempDir, err := fsMock.MkdirTemp(".tmp") + assert.NoError(t, err) + _, err = sootHandler.initializeSootWrapper(fsMock, tempDir) + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestInitializeSootWrapperFsReadAllError(t *testing.T) { + errString := "fs read all error" + fsMock := ioTestData.FileSystemMock{FsReadAllError: fmt.Errorf(errString)} //nolint + tempDir, err := fsMock.MkdirTemp(".tmp") + assert.NoError(t, err) + _, err = sootHandler.initializeSootWrapper(fsMock, tempDir) + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestInitializeSootWrapperFsWriteFileError(t *testing.T) { + errString := "fs write file error" + fsMock := ioTestData.FileSystemMock{FsWriteFileError: fmt.Errorf(errString)} //nolint + tempDir, err := fsMock.MkdirTemp(".tmp") + assert.NoError(t, err) + _, err = sootHandler.initializeSootWrapper(fsMock, tempDir) + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestDownloadSootWrapper(t *testing.T) { + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{} + err := sootHandler.downloadSootWrapper(arcMock, fsMock, "soot-wrapper.jar", "11") + assert.NoError(t, err, "expected no error for downloading soot-wrapper jar") +} + +func TestDownloadSootWrapperMkdirTempError(t *testing.T) { + errString := "mkdir temp error" + fsMock := ioTestData.FileSystemMock{MkdirTempError: fmt.Errorf(errString)} //nolint + arcMock := ioTestData.ArchiveMock{} + err := sootHandler.downloadSootWrapper(arcMock, fsMock, "soot-wrapper.jar", "11") + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestDownloadSootWrapperCreateError(t *testing.T) { + errString := "create error" + fsMock := ioTestData.FileSystemMock{CreateError: fmt.Errorf(errString)} //nolint + arcMock := ioTestData.ArchiveMock{} + err := sootHandler.downloadSootWrapper(arcMock, fsMock, "soot-wrapper.jar", "11") + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestDownloadSootWrapperUnzipError(t *testing.T) { + errString := "create error" + fsMock := ioTestData.FileSystemMock{} + arcMock := ioTestData.ArchiveMock{UnzipFileError: fmt.Errorf(errString)} //nolint + err := sootHandler.downloadSootWrapper(arcMock, fsMock, "soot-wrapper.jar", "11") + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestDownloadCompressedSootWrapper(t *testing.T) { + fs := ioFs.FileSystem{} + dir, err := fs.MkdirTemp(".test_tmp") + assert.NoError(t, err, "trying to make temp dir") + path := dir + "/soot_wrapper.zip" + file, err := fs.Create(path) + assert.NoError(t, err, "trying to create file") + defer file.Close() + + err = sootHandler.downloadCompressedSootWrapper(fs, file, "11") + assert.NoError(t, err, "expected no error for downloading soot-wrapper") +} + +func TestGetSootWrapper(t *testing.T) { + fs := ioTestData.FileSystemMock{} + arc := ioTestData.ArchiveMock{} + tests := []struct { + name string + version string + expectError bool + }{ + { + name: "Unsupported version", + version: "8", + expectError: true, + }, + { + name: "Version 11", + version: "11", + expectError: false, + }, + { + name: "Version 17", + version: "17", + expectError: false, + }, + { + name: "Version 21", + version: "21", + expectError: false, + }, + { + name: "Version not int", + version: "akjwdm", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := sootHandler.GetSootWrapper(tt.version, fs, arc) + + if tt.expectError { + assert.Error(t, err) + assert.Empty(t, result) + } else { + assert.NoError(t, err) + assert.NotEmpty(t, result) + } + }) + } +} + +func TestGetSootWrapperDownload(t *testing.T) { + fsMock := ioTestData.FileSystemMock{StatError: fmt.Errorf(""), IsNotExistBool: true} + arcMock := ioTestData.ArchiveMock{} + sootHandler := SootHandler{} + _, err := sootHandler.GetSootWrapper("17", fsMock, arcMock) + assert.NoError(t, err) +} + +func TestGetSootWrapperInitialize(t *testing.T) { + fsMock := ioTestData.FileSystemMock{StatError: fmt.Errorf(""), IsNotExistBool: true} + arcMock := ioTestData.ArchiveMock{} + sootHandler := SootHandler{} + _, err := sootHandler.GetSootWrapper("23", fsMock, arcMock) + assert.NoError(t, err) +} + +func TestGetSootWrapperMkdirError(t *testing.T) { + errString := "mkdir error" + fsMock := ioTestData.FileSystemMock{MkdirError: fmt.Errorf(errString), StatError: fmt.Errorf(""), IsNotExistBool: true} //nolint + arcMock := ioTestData.ArchiveMock{} + sootHandler := SootHandler{} + _, err := sootHandler.GetSootWrapper("11", fsMock, arcMock) + assert.Error(t, err) + assert.Equal(t, err.Error(), errString) +} + +func TestGetSootHandlerJavaVersion(t *testing.T) { + sootHandler := SootHandler{} + tests := []struct { + name string + version int + expectedVersion string + expectError bool + }{ + { + name: "Unsupported version", + version: 8, + expectedVersion: "", + expectError: true, + }, + { + name: "Version 11", + version: 11, + expectedVersion: "11", + expectError: false, + }, + { + name: "Version 17", + version: 17, + expectedVersion: "17", + expectError: false, + }, + { + name: "Version 21", + version: 21, + expectedVersion: "21", + expectError: false, + }, + { + name: "Version not int", + version: 12, + expectedVersion: "11", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := sootHandler.getSootHandlerJavaVersion(tt.version) + + if tt.expectError { + assert.Error(t, err) + assert.Empty(t, result) + } else { + assert.NoError(t, err) + assert.NotEmpty(t, result) + } + }) + } +} diff --git a/internal/callgraph/language/java11/strategy.go b/internal/callgraph/language/java/strategy.go similarity index 95% rename from internal/callgraph/language/java11/strategy.go rename to internal/callgraph/language/java/strategy.go index bd7760d0..96801369 100644 --- a/internal/callgraph/language/java11/strategy.go +++ b/internal/callgraph/language/java/strategy.go @@ -98,6 +98,7 @@ func (s Strategy) Invoke() ([]job.IJob, error) { s.config, s.ctx, io.FileSystem{}, + SootHandler{s.config.Version()}, ), ) } @@ -105,7 +106,14 @@ func (s Strategy) Invoke() ([]job.IJob, error) { return jobs, nil } -func NewStrategy(config conf.IConfig, paths []string, exclusions []string, inclusions []string, finder finder.IFinder, ctx cgexec.IContext) Strategy { +func NewStrategy( + config conf.IConfig, + paths []string, + exclusions []string, + inclusions []string, + finder finder.IFinder, + ctx cgexec.IContext, +) Strategy { return Strategy{config, CmdFactory{}, paths, exclusions, inclusions, finder, ctx} } diff --git a/internal/callgraph/language/java11/strategy_test.go b/internal/callgraph/language/java/strategy_test.go similarity index 92% rename from internal/callgraph/language/java11/strategy_test.go rename to internal/callgraph/language/java/strategy_test.go index 25281e28..8d39d3c4 100644 --- a/internal/callgraph/language/java11/strategy_test.go +++ b/internal/callgraph/language/java/strategy_test.go @@ -8,7 +8,7 @@ import ( ctxTestdata "github.com/debricked/cli/internal/callgraph/cgexec/testdata" "github.com/debricked/cli/internal/callgraph/config" "github.com/debricked/cli/internal/callgraph/finder/testdata" - javaTestdata "github.com/debricked/cli/internal/callgraph/language/java11/testdata" + javaTestdata "github.com/debricked/cli/internal/callgraph/language/java/testdata" "github.com/stretchr/testify/assert" ) @@ -25,7 +25,7 @@ func TestNewStrategy(t *testing.T) { s = NewStrategy(nil, []string{"file-1", "file-2"}, []string{}, []string{}, nil, nil) assert.NotNil(t, s) - conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven", "v2.0.0") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1"} finder.FindRootsNames = testFiles @@ -42,7 +42,7 @@ func TestInvokeNoFiles(t *testing.T) { } func TestInvokeOneFile(t *testing.T) { - conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven", "v2.0.0") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1"} finder.FindRootsNames = testFiles @@ -53,7 +53,7 @@ func TestInvokeOneFile(t *testing.T) { } func TestInvokeManyFiles(t *testing.T) { - conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven", "v2.0.0") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2"} finder.FindRootsNames = testFiles @@ -64,7 +64,7 @@ func TestInvokeManyFiles(t *testing.T) { } func TestInvokeManyFilesWCorrectFilters(t *testing.T) { - conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, false, "maven") + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, false, "maven", "v2.0.0") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2", "file-3"} finder.FindRootsNames = []string{"file-3/pom.xml"} @@ -83,7 +83,7 @@ func TestInvokeManyFilesWCorrectFilters(t *testing.T) { } func TestBuildProjectsError(t *testing.T) { - conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, false, "maven") + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, false, "maven", "v2.0.0") finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2", "file-3"} finder.FindRootsNames = []string{"file-3/pom.xml"} diff --git a/internal/callgraph/language/java11/testdata/callgraph_mock.go b/internal/callgraph/language/java/testdata/callgraph_mock.go similarity index 100% rename from internal/callgraph/language/java11/testdata/callgraph_mock.go rename to internal/callgraph/language/java/testdata/callgraph_mock.go diff --git a/internal/callgraph/language/java11/testdata/cmd_factory_mock.go b/internal/callgraph/language/java/testdata/cmd_factory_mock.go similarity index 60% rename from internal/callgraph/language/java11/testdata/cmd_factory_mock.go rename to internal/callgraph/language/java/testdata/cmd_factory_mock.go index 3aa4b091..a38a0161 100644 --- a/internal/callgraph/language/java11/testdata/cmd_factory_mock.go +++ b/internal/callgraph/language/java/testdata/cmd_factory_mock.go @@ -4,6 +4,7 @@ import ( "os/exec" "github.com/debricked/cli/internal/callgraph/cgexec" + ioFs "github.com/debricked/cli/internal/io" ) type CmdFactoryMock struct { @@ -13,6 +14,8 @@ type CmdFactoryMock struct { CallGraphGenErr error BuildMavenName string BuildMavenErr error + JavaVersionName string + JavaVersionErr error } func NewEchoCmdFactory() CmdFactoryMock { @@ -20,6 +23,7 @@ func NewEchoCmdFactory() CmdFactoryMock { MvnCopyDepName: "echo", CallGraphGenName: "echo", BuildMavenName: "echo", + JavaVersionName: "echo", } } @@ -34,3 +38,18 @@ func (f CmdFactoryMock) MakeCallGraphGenerationCmd(_ string, _ string, _ []strin func (f CmdFactoryMock) MakeBuildMavenCmd(workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) { return exec.Command(f.BuildMavenName, "BuildMaven"), f.BuildMavenErr } + +func (f CmdFactoryMock) MakeJavaVersionCmd(workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) { + return exec.Command( + f.JavaVersionName, + "\"openjdk 23.0.1 2024-10-15\nOpenJDK Runtime Environment Homebrew (build 23.0.1)\nOpenJDK 64-Bit Server VM Homebrew (build 23.0.1, mixed mode, sharing)\"", + ), f.JavaVersionErr +} + +type MockSootHandler struct { + GetSootWrapperError error +} + +func (msh MockSootHandler) GetSootWrapper(version string, fs ioFs.IFileSystem, arc ioFs.IArchive) (string, error) { + return "", msh.GetSootWrapperError +} diff --git a/internal/callgraph/language/java11/callgraph_test.go b/internal/callgraph/language/java11/callgraph_test.go deleted file mode 100644 index 179d01cc..00000000 --- a/internal/callgraph/language/java11/callgraph_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package java - -import ( - "fmt" - "testing" - - "github.com/debricked/cli/internal/callgraph/language/java11/testdata" - ioTestData "github.com/debricked/cli/internal/io/testdata" - "github.com/stretchr/testify/assert" -) - -func TestRunCallGraphWithSetupMock(t *testing.T) { - - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraphWithSetup() - - assert.Nil(t, err) -} - -func TestFsOpenEmbedError(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{FsOpenEmbedError: fmt.Errorf("error")} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraphWithSetup() - - assert.NotNil(t, err) -} - -func TestMkdirTempError(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{MkdirTempError: fmt.Errorf("error")} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraphWithSetup() - - assert.NotNil(t, err) -} - -func TestReadAllError(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{FsReadAllError: fmt.Errorf("error")} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraphWithSetup() - - assert.NotNil(t, err) -} - -func TestWriteFileError(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{FsWriteFileError: fmt.Errorf("error")} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraphWithSetup() - - assert.NotNil(t, err) -} - -func TestRunCallGraphMock(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - fsMock := ioTestData.FileSystemMock{} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraph(".") - - assert.Nil(t, err) -} - -func TestRunCallGraphErrorMock(t *testing.T) { - cmdMock := testdata.NewEchoCmdFactory() - cmdMock.CallGraphGenErr = fmt.Errorf("error") - fsMock := ioTestData.FileSystemMock{} - cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) - - err := cg.RunCallGraph(".") - - assert.NotNil(t, err) -} diff --git a/internal/callgraph/language/language.go b/internal/callgraph/language/language.go index f2551a9a..732cc7bc 100644 --- a/internal/callgraph/language/language.go +++ b/internal/callgraph/language/language.go @@ -1,6 +1,6 @@ package language -import java "github.com/debricked/cli/internal/callgraph/language/java11" +import "github.com/debricked/cli/internal/callgraph/language/java" type ILanguage interface { Name() string diff --git a/internal/callgraph/strategy/strategy_factory.go b/internal/callgraph/strategy/strategy_factory.go index 97da8154..596d0b89 100644 --- a/internal/callgraph/strategy/strategy_factory.go +++ b/internal/callgraph/strategy/strategy_factory.go @@ -8,7 +8,7 @@ import ( golangfinder "github.com/debricked/cli/internal/callgraph/finder/golangfinder" "github.com/debricked/cli/internal/callgraph/finder/javafinder" "github.com/debricked/cli/internal/callgraph/language/golang" - java "github.com/debricked/cli/internal/callgraph/language/java11" + "github.com/debricked/cli/internal/callgraph/language/java" ) type IFactory interface { @@ -21,7 +21,13 @@ func NewStrategyFactory() Factory { return Factory{} } -func (sf Factory) Make(config conf.IConfig, paths []string, exclusions []string, inclusions []string, ctx cgexec.IContext) (IStrategy, error) { +func (sf Factory) Make( + config conf.IConfig, + paths []string, + exclusions []string, + inclusions []string, + ctx cgexec.IContext, +) (IStrategy, error) { name := config.Language() switch name { case java.Name: diff --git a/internal/callgraph/strategy/strategy_factory_test.go b/internal/callgraph/strategy/strategy_factory_test.go index 15172fd1..76f9bf17 100644 --- a/internal/callgraph/strategy/strategy_factory_test.go +++ b/internal/callgraph/strategy/strategy_factory_test.go @@ -5,7 +5,7 @@ import ( "github.com/debricked/cli/internal/callgraph/config" "github.com/debricked/cli/internal/callgraph/finder/javafinder" - java "github.com/debricked/cli/internal/callgraph/language/java11" + "github.com/debricked/cli/internal/callgraph/language/java" "github.com/stretchr/testify/assert" ) @@ -16,14 +16,14 @@ func TestNewStrategyFactory(t *testing.T) { func TestMakeErr(t *testing.T) { f := NewStrategyFactory() - conf := config.NewConfig("test", nil, nil, true, "") + conf := config.NewConfig("test", nil, nil, true, "", "") s, err := f.Make(conf, nil, nil, nil, nil) assert.Nil(t, s) assert.ErrorContains(t, err, "failed to make strategy from test") } func TestMake(t *testing.T) { - conf := config.NewConfig(java.Name, nil, nil, true, "") + conf := config.NewConfig(java.Name, nil, nil, true, "", "") cases := map[string]IStrategy{ java.Name: java.NewStrategy(conf, []string{}, []string{}, []string{}, javafinder.JavaFinder{}, nil), } diff --git a/internal/callgraph/strategy/testdata/strategy_mock_factory.go b/internal/callgraph/strategy/testdata/strategy_mock_factory.go index 2cba3b70..33ee6357 100644 --- a/internal/callgraph/strategy/testdata/strategy_mock_factory.go +++ b/internal/callgraph/strategy/testdata/strategy_mock_factory.go @@ -13,7 +13,13 @@ func NewStrategyFactoryMock() FactoryMock { return FactoryMock{} } -func (sf FactoryMock) Make(config config.IConfig, paths []string, exclusions []string, inclusions []string, ctx cgexec.IContext) (strategy.IStrategy, error) { +func (sf FactoryMock) Make( + config config.IConfig, + paths []string, + exclusions []string, + inclusions []string, + ctx cgexec.IContext, +) (strategy.IStrategy, error) { return NewStrategyMock(config, paths, testdata.FinderMock{}, ctx), nil } @@ -23,6 +29,12 @@ func NewStrategyFactoryErrorMock() FactoryErrorMock { return FactoryErrorMock{} } -func (sf FactoryErrorMock) Make(config config.IConfig, paths []string, exclusions []string, inclusions []string, ctx cgexec.IContext) (strategy.IStrategy, error) { +func (sf FactoryErrorMock) Make( + config config.IConfig, + paths []string, + exclusions []string, + inclusions []string, + ctx cgexec.IContext, +) (strategy.IStrategy, error) { return NewStrategyErrorMock(config, paths, testdata.FinderMock{}, ctx), nil } diff --git a/internal/cmd/callgraph/callgraph.go b/internal/cmd/callgraph/callgraph.go index b8d970c5..aab282cf 100644 --- a/internal/cmd/callgraph/callgraph.go +++ b/internal/cmd/callgraph/callgraph.go @@ -127,9 +127,10 @@ func RunE(callgraph callgraph.IGenerator) func(_ *cobra.Command, args []string) } configs := []conf.IConfig{} + version := viper.GetString("cliVersion") for _, language := range languages { - configs = append(configs, conf.NewConfig(language, args, map[string]string{}, !buildDisabled, languageMap[language])) + configs = append(configs, conf.NewConfig(language, args, map[string]string{}, !buildDisabled, languageMap[language], version)) } options := cg.DebrickedOptions{ diff --git a/internal/cmd/root/root.go b/internal/cmd/root/root.go index d51ad765..a917032c 100644 --- a/internal/cmd/root/root.go +++ b/internal/cmd/root/root.go @@ -29,6 +29,7 @@ Complete documentation is available at https://docs.debricked.com/tools-and-inte }, Version: version, } + viper.Set("cliVersion", version) viper.SetEnvPrefix("DEBRICKED") viper.AutomaticEnv() viper.MustBindEnv(AccessTokenFlag) diff --git a/internal/cmd/root/root_test.go b/internal/cmd/root/root_test.go index 5541b033..7f576af2 100644 --- a/internal/cmd/root/root_test.go +++ b/internal/cmd/root/root_test.go @@ -35,7 +35,7 @@ func TestNewRootCmd(t *testing.T) { } } assert.Truef(t, match, "failed to assert that flag was present: "+OldAccessTokenFlag) - assert.Len(t, viperKeys, 22) + assert.Len(t, viperKeys, 23) } func TestPreRun(t *testing.T) { diff --git a/internal/io/archive.go b/internal/io/archive.go index 454923b4..553b07da 100644 --- a/internal/io/archive.go +++ b/internal/io/archive.go @@ -2,11 +2,13 @@ package io import ( "encoding/base64" + "fmt" "path" ) type IArchive interface { ZipFile(sourcePath string, targetPath string, zippedName string) error + UnzipFile(sourcePath string, targetPath string) error B64(sourceName string, targetName string) error Cleanup(targetName string) error } @@ -40,16 +42,14 @@ func (arc *Archive) ZipFile(sourcePath string, targetPath string, zippedName str return err } - zipFile, err := fs.Create(targetPath) if err != nil { return err } defer fs.CloseFile(zipFile) - zipWriter := zip.NewWriter(zipFile) - defer zip.Close(zipWriter) + defer zip.CloseWriter(zipWriter) //nolint info, err := fs.StatFile(zipFile) if err != nil { @@ -81,6 +81,36 @@ func (arc *Archive) ZipFile(sourcePath string, targetPath string, zippedName str return err } +func (arc *Archive) UnzipFile(sourcePath string, targetPath string) error { + r, err := arc.zip.OpenReader(sourcePath) + if err != nil { + + return err + } + defer arc.zip.CloseReader(r) //nolint + + if len(r.File) != 1 { + return fmt.Errorf("cannot unzip archive which does not contain exactly one file") + } + + f := r.File[0] + outFile, err := arc.fs.Create(targetPath) + if err != nil { + return err + } + defer outFile.Close() //nolint + + rc, err := arc.zip.Open(f) + if err != nil { + return err + } + defer arc.zip.Close(rc) //nolint + + _, err = arc.fs.Copy(outFile, rc) + + return err +} + func (arc *Archive) B64(sourceName string, targetName string) error { fs := arc.fs fileContent, err := fs.ReadFile(path.Join(arc.workingDirectory, sourceName)) diff --git a/internal/io/archive_test.go b/internal/io/archive_test.go index fc82e16d..6f6454f8 100644 --- a/internal/io/archive_test.go +++ b/internal/io/archive_test.go @@ -1,6 +1,7 @@ package io import ( + "archive/zip" "fmt" "testing" @@ -119,6 +120,19 @@ func TestWriteToWriterError(t *testing.T) { assert.Equal(t, err.Error(), "error") } +func TestUnzipFileError(t *testing.T) { + fsMock := ioTestData.FileSystemMock{} + zipMock := ioTestData.ZipMock{OpenReaderError: fmt.Errorf("error")} + a := Archive{ + workingDirectory: "nonexisting", + fs: fsMock, + zip: zipMock, + } + err := a.UnzipFile("test_source_path", "test_target_path") + assert.NotNil(t, err) + assert.Equal(t, err.Error(), "error") +} + func TestB64(t *testing.T) { fsMock := ioTestData.FileSystemMock{} a := Archive{ @@ -176,3 +190,85 @@ func TestCleanup(t *testing.T) { err := a.Cleanup("testdir") assert.Nil(t, err) } + +func TestUnzipFileReadFileError(t *testing.T) { + fsMock := ioTestData.FileSystemMock{} + zipMock := ioTestData.ZipMock{OpenReaderError: fmt.Errorf("%s", t.Name())} + a := Archive{ + workingDirectory: ".", + fs: fsMock, + zip: zipMock, + } + err := a.UnzipFile("", "") + assert.Error(t, err) + assert.Equal(t, err.Error(), t.Name()) +} + +func TestUnzipFileCreateError(t *testing.T) { + reader := zip.Reader{ + File: []*zip.File{nil}, + } + readCloser := zip.ReadCloser{Reader: reader} //nolint + fsMock := ioTestData.FileSystemMock{CreateError: fmt.Errorf("%s", t.Name())} + zipMock := ioTestData.ZipMock{ReaderCloser: &readCloser} + a := Archive{ + workingDirectory: ".", + fs: fsMock, + zip: zipMock, + } + err := a.UnzipFile("", "") + assert.Error(t, err) + assert.Equal(t, err.Error(), t.Name()) +} + +func TestUnzipFileOpenError(t *testing.T) { + reader := zip.Reader{ + File: []*zip.File{nil}, + } + readCloser := zip.ReadCloser{Reader: reader} //nolint + fsMock := ioTestData.FileSystemMock{} + zipMock := ioTestData.ZipMock{ReaderCloser: &readCloser, OpenError: fmt.Errorf("%s", t.Name())} + a := Archive{ + workingDirectory: ".", + fs: fsMock, + zip: zipMock, + } + err := a.UnzipFile("", "") + assert.Error(t, err) + assert.Equal(t, err.Error(), t.Name()) +} +func TestUnzipFileCopyError(t *testing.T) { + r, err := zipStruct.OpenReader("testdata/text.zip") + assert.NoError(t, err) + defer zipStruct.CloseReader(r) //nolint + + fsMock := ioTestData.FileSystemMock{CopyError: fmt.Errorf("%s", t.Name())} + zipMock := ioTestData.ZipMock{ReaderCloser: r} + a := Archive{ + workingDirectory: ".", + fs: fsMock, + zip: zipMock, + } + err = a.UnzipFile("", "") + assert.Error(t, err) + assert.Equal(t, err.Error(), t.Name()) +} + +func TestUnzipFileNotSingleFile(t *testing.T) { + reader := zip.Reader{ + File: []*zip.File{nil, nil}, + } + readCloser := zip.ReadCloser{Reader: reader} //nolint + defer zipStruct.CloseReader(&readCloser) //nolint + + fsMock := ioTestData.FileSystemMock{} + zipMock := ioTestData.ZipMock{ReaderCloser: &readCloser} + a := Archive{ + workingDirectory: ".", + fs: fsMock, + zip: zipMock, + } + err := a.UnzipFile("", "") + assert.Error(t, err) + assert.Equal(t, err.Error(), "cannot unzip archive which does not contain exactly one file") +} diff --git a/internal/io/file_system.go b/internal/io/file_system.go index e6e7f914..8320376b 100644 --- a/internal/io/file_system.go +++ b/internal/io/file_system.go @@ -23,6 +23,8 @@ type IFileSystem interface { FsCloseFile(file fs.File) FsReadAll(file fs.File) ([]byte, error) FsWriteFile(path string, bytes []byte, perm fs.FileMode) error + Mkdir(path string, perm fs.FileMode) error + Copy(destination io.Writer, source io.Reader) (int64, error) } type FileSystem struct{} @@ -67,6 +69,10 @@ func (_ FileSystem) MkdirTemp(pattern string) (string, error) { return os.MkdirTemp("", pattern) } +func (_ FileSystem) Mkdir(name string, perm fs.FileMode) error { + return os.Mkdir(name, perm) +} + func (_ FileSystem) RemoveAll(path string) { os.RemoveAll(path) } @@ -86,3 +92,7 @@ func (_ FileSystem) FsReadAll(file fs.File) ([]byte, error) { func (_ FileSystem) FsWriteFile(path string, bytes []byte, perm fs.FileMode) error { return os.WriteFile(path, bytes, perm) } + +func (_ FileSystem) Copy(destination io.Writer, source io.Reader) (int64, error) { + return io.Copy(destination, source) +} diff --git a/internal/io/file_system_test.go b/internal/io/file_system_test.go index e0ec06ad..ac6653fe 100644 --- a/internal/io/file_system_test.go +++ b/internal/io/file_system_test.go @@ -165,3 +165,38 @@ func TestWriteFile(t *testing.T) { assert.Nil(t, err) } + +func TestMkdir(t *testing.T) { + fn := fileNameFS + t.Name() + err := filesystem.Mkdir(fn, 0755) + assert.NoError(t, err) + _, err = filesystem.Stat(fn) + assert.NoError(t, err) + filesystem.RemoveAll(fn) + _, err = filesystem.Stat(fn) + assert.Error(t, err) +} + +func TestCopy(t *testing.T) { + fn_source := fileNameFS + t.Name() + "source" + fn_target := fileNameFS + t.Name() + "target" + err := filesystem.FsWriteFile(fn_source, []byte{}, 0600) + assert.NoError(t, err) + target, err := filesystem.Create(fn_target) + assert.NoError(t, err) + source, err := filesystem.Open(fn_source) + assert.NoError(t, err) + + _, err = filesystem.Copy(target, source) + assert.NoError(t, err) + + // Remove generated testfiles + filesystem.CloseFile(source) + filesystem.RemoveAll(fn_source) + _, err = filesystem.Stat(fn_source) + assert.Error(t, err) + filesystem.CloseFile(target) + filesystem.RemoveAll(fn_target) + _, err = filesystem.Stat(fn_target) + assert.Error(t, err) +} diff --git a/internal/io/testdata/archive_mock.go b/internal/io/testdata/archive_mock.go index d951a168..fc5ab82a 100644 --- a/internal/io/testdata/archive_mock.go +++ b/internal/io/testdata/archive_mock.go @@ -3,11 +3,12 @@ package testdata import "strings" type ArchiveMock struct { - ZipFileError error - B64Error error - CleanupError error - PathError error - Dir string + ZipFileError error + B64Error error + CleanupError error + PathError error + Dir string + UnzipFileError error } func (am ArchiveMock) ZipFile(sourceName string, targetName string, zipName string) error { @@ -26,3 +27,7 @@ func (am ArchiveMock) B64(sourceName string, targetName string) error { func (am ArchiveMock) Cleanup(fileName string) error { return am.CleanupError } + +func (am ArchiveMock) UnzipFile(sourcePath string, targetPath string) error { + return am.UnzipFileError +} diff --git a/internal/io/testdata/file_system_mock.go b/internal/io/testdata/file_system_mock.go index 562acc72..76fe4ff5 100644 --- a/internal/io/testdata/file_system_mock.go +++ b/internal/io/testdata/file_system_mock.go @@ -23,6 +23,9 @@ type FileSystemMock struct { FsOpenEmbedError error FsReadAllError error FsWriteFileError error + MkdirError error + CopyError error + CopySize int64 } func (fsm FileSystemMock) Open(path string) (*os.File, error) { @@ -64,6 +67,10 @@ func (fsm FileSystemMock) MkdirTemp(pattern string) (string, error) { return pattern, fsm.MkdirTempError } +func (fsm FileSystemMock) Mkdir(name string, perm fs.FileMode) error { + return fsm.MkdirError +} + func (fsm FileSystemMock) RemoveAll(path string) { } @@ -81,3 +88,7 @@ func (fsm FileSystemMock) FsReadAll(file fs.File) ([]byte, error) { func (fsm FileSystemMock) FsWriteFile(path string, bytes []byte, perm fs.FileMode) error { return fsm.FsWriteFileError } + +func (fsm FileSystemMock) Copy(destination io.Writer, source io.Reader) (int64, error) { + return fsm.CopySize, fsm.CopyError +} diff --git a/internal/io/testdata/text.txt b/internal/io/testdata/text.txt new file mode 100644 index 00000000..f3a34851 --- /dev/null +++ b/internal/io/testdata/text.txt @@ -0,0 +1 @@ +text \ No newline at end of file diff --git a/internal/io/testdata/text.zip b/internal/io/testdata/text.zip new file mode 100644 index 00000000..917705aa Binary files /dev/null and b/internal/io/testdata/text.zip differ diff --git a/internal/io/testdata/zip_mock.go b/internal/io/testdata/zip_mock.go index 7111f54f..fcc18bbf 100644 --- a/internal/io/testdata/zip_mock.go +++ b/internal/io/testdata/zip_mock.go @@ -11,9 +11,16 @@ type ZipMock struct { writer *zip.Writer // fileHeader *zip.FileHeader createHeader io.Writer + ReaderCloser *zip.ReadCloser FileHeaderError error CreateHeaderError error + CloseWriterError error + OpenReaderError error + CloseReaderError error + OpenError error CloseError error + ReadError error + ReadCloser io.ReadCloser } func (zm ZipMock) NewWriter(file *os.File) *zip.Writer { @@ -24,7 +31,16 @@ func (zm ZipMock) FileInfoHeader(fileInfo fs.FileInfo) (*zip.FileHeader, error) return &zip.FileHeader{}, zm.FileHeaderError } -func (_ ZipMock) GetDeflate() uint16 { +func (zm ZipMock) OpenReader(source string) (*zip.ReadCloser, error) { + + return zm.ReaderCloser, zm.OpenReaderError +} + +func (zm ZipMock) CloseReader(reader *zip.ReadCloser) error { + return zm.CloseReaderError +} + +func (zm ZipMock) GetDeflate() uint16 { return zip.Deflate } @@ -32,6 +48,28 @@ func (zm ZipMock) CreateHeader(writer *zip.Writer, header *zip.FileHeader) (io.W return zm.createHeader, zm.CreateHeaderError } -func (zm ZipMock) Close(writer *zip.Writer) error { +func (zm ZipMock) CloseWriter(writer *zip.Writer) error { + return zm.CloseWriterError +} + +func (zm ZipMock) Open(file *zip.File) (io.ReadCloser, error) { + return zm.ReadCloser, zm.OpenError +} + +func (zm ZipMock) Close(rc io.ReadCloser) error { return zm.CloseError } + +type MockReader struct { + ReadBytes int + ReadError error +} + +func (r *MockReader) Read(p []byte) (int, error) { + return r.ReadBytes, r.ReadError +} + +func MockFileSlice(contents []string) []*zip.File { + files := []*zip.File{} + return files +} diff --git a/internal/io/zip.go b/internal/io/zip.go index b63065bd..721aa48d 100644 --- a/internal/io/zip.go +++ b/internal/io/zip.go @@ -12,20 +12,24 @@ type IZip interface { FileInfoHeader(fileInfo fs.FileInfo) (*zip.FileHeader, error) GetDeflate() uint16 CreateHeader(writer *zip.Writer, header *zip.FileHeader) (io.Writer, error) - Close(writer *zip.Writer) error + CloseWriter(writer *zip.Writer) error + Open(file *zip.File) (io.ReadCloser, error) + Close(io.ReadCloser) error + OpenReader(source string) (*zip.ReadCloser, error) + CloseReader(reader *zip.ReadCloser) error } type Zip struct{} -func (_ Zip) NewWriter(file *os.File) *zip.Writer { +func (z Zip) NewWriter(file *os.File) *zip.Writer { return zip.NewWriter(file) } -func (_ Zip) FileInfoHeader(fileInfo fs.FileInfo) (*zip.FileHeader, error) { +func (z Zip) FileInfoHeader(fileInfo fs.FileInfo) (*zip.FileHeader, error) { return zip.FileInfoHeader(fileInfo) } -func (_ Zip) GetDeflate() uint16 { +func (z Zip) GetDeflate() uint16 { return zip.Deflate } @@ -33,6 +37,22 @@ func (z Zip) CreateHeader(writer *zip.Writer, header *zip.FileHeader) (io.Writer return writer.CreateHeader(header) } -func (z Zip) Close(writer *zip.Writer) error { +func (z Zip) CloseWriter(writer *zip.Writer) error { return writer.Close() } + +func (z Zip) Open(file *zip.File) (io.ReadCloser, error) { + return file.Open() +} + +func (z Zip) Close(rc io.ReadCloser) error { + return rc.Close() +} + +func (z Zip) OpenReader(source string) (*zip.ReadCloser, error) { + return zip.OpenReader(source) +} + +func (z Zip) CloseReader(reader *zip.ReadCloser) error { + return reader.Close() +} diff --git a/internal/io/zip_test.go b/internal/io/zip_test.go index d158156e..85af601d 100644 --- a/internal/io/zip_test.go +++ b/internal/io/zip_test.go @@ -12,11 +12,11 @@ const fileNameZip = "debricked-test.zip" func TestNewWriter(t *testing.T) { testFile, err := filesystem.Create(fileNameZip) - defer deleteFile(t, testFile) + defer deleteFile(t, testFile) //nolint assert.NoError(t, err) writer := zipStruct.NewWriter(testFile) - err = zipStruct.Close(writer) + err = zipStruct.CloseWriter(writer) assert.NoError(t, err) } @@ -26,7 +26,7 @@ func TestFileInfoHeader(t *testing.T) { assert.NoError(t, err) writer := zipStruct.NewWriter(testFile) - defer zipStruct.Close(writer) + defer zipStruct.CloseWriter(writer) //nolint info, _ := filesystem.StatFile(testFile) _, err = zipStruct.FileInfoHeader(info) @@ -36,11 +36,11 @@ func TestFileInfoHeader(t *testing.T) { func TestCreateHeader(t *testing.T) { testFile, err := filesystem.Create(fileNameZip) - defer deleteFile(t, testFile) + defer deleteFile(t, testFile) //nolint assert.NoError(t, err) writer := zipStruct.NewWriter(testFile) - defer zipStruct.Close(writer) + defer zipStruct.CloseWriter(writer) //nolint info, _ := filesystem.StatFile(testFile) header, _ := zipStruct.FileInfoHeader(info) @@ -53,3 +53,43 @@ func TestDeflate(t *testing.T) { deflate := zipStruct.GetDeflate() assert.Equal(t, deflate, uint16(8)) } + +func TestOpenZip(t *testing.T) { + r, err := zipStruct.OpenReader("testdata/text.zip") + assert.NoError(t, err) + defer zipStruct.CloseReader(r) //nolint + + assert.NotNil(t, r, "reader not opened properly") + _, err = zipStruct.Open(r.File[0]) + assert.NoError(t, err, "should be able to open zip file") +} + +func TestCloseZip(t *testing.T) { + r, err := zipStruct.OpenReader("testdata/text.zip") + assert.NoError(t, err) + defer zipStruct.CloseReader(r) //nolint + + assert.NotNil(t, r, "reader not opened properly") + rc, err := zipStruct.Open(r.File[0]) + assert.NoError(t, err, "should be able to open zip file") + + err = zipStruct.Close(rc) + assert.NoError(t, err, "could not close ReadCloser with zip.Close() properly") +} + +func TestOpenCloseReader(t *testing.T) { + arc := NewArchive(".") + testFileName := "testdata/ziptest.zip" + err := arc.ZipFile("testdata/text.txt", testFileName, "ziptest.zip") + assert.NoError(t, err) + + testFile, err := filesystem.Open(testFileName) + defer deleteFile(t, testFile) + assert.NoError(t, err) + + rc, err := zipStruct.OpenReader(testFileName) + assert.NotNil(t, rc, "OpenReader did not create a viable ReadCloser") + assert.NoError(t, err, "OpenReader did not properly open ReadCloser") + err = zipStruct.CloseReader(rc) + assert.NoError(t, err, "CloseReader did not properly close ReadCloser") +} diff --git a/internal/scan/scanner.go b/internal/scan/scanner.go index d414946e..373a21c0 100644 --- a/internal/scan/scanner.go +++ b/internal/scan/scanner.go @@ -74,6 +74,7 @@ type DebrickedOptions struct { MinFingerprintContentLength int TagCommitAsRelease bool Experimental bool + Version string } func NewDebrickedScanner( @@ -241,8 +242,8 @@ func (dScanner *DebrickedScanner) scan(options DebrickedOptions, gitMetaObject g if options.CallGraph { debug.Log("Running scanFingerprint...", options.Debug) configs := []config.IConfig{ - config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven"), - config.NewConfig("golang", []string{}, map[string]string{"pm": "go"}, true, "go"), + config.NewConfig("java", []string{}, map[string]string{"pm": "maven"}, true, "maven", options.Version), + config.NewConfig("golang", []string{}, map[string]string{"pm": "go"}, true, "go", options.Version), } timeout := options.CallGraphGenerateTimeout path := options.Path diff --git a/scripts/install.sh b/scripts/install.sh index d6cf98e0..7c6907a4 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -8,9 +8,9 @@ then echo -e "Failed to find git, thus also the version. Version will be set to v0.0.0" fi set +e -version=$(git symbolic-ref -q --short HEAD || git describe --tags --exact-match) +version=${DEBRICKED_VERSION:-$(git symbolic-ref -q --short HEAD || git describe --tags --exact-match)} set -e -ldFlags="-s -w -X main.version=${version}" +ldFlags="-X main.version=${version}" go install -ldflags "${ldFlags}" ./cmd/debricked go generate -v -x ./cmd/debricked go build -ldflags "${ldFlags}" ./cmd/debricked