Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ test:
test-e2e:
bash scripts/test_e2e.sh $(type)

# Build the SootUp fat JAR from source.
# The built artifact is placed in:
# build/sootup/SootUpWrapper/target/sootup-wrapper-1.0.0.jar
.PHONY: build-sootup-jar
build-sootup-jar:
cd build/sootup/SootUpWrapper && mvn clean package -q

# Build the SootUp fat JAR AND copy it into the Go CLI embedded directory so
# that it is picked up by the //go:embed directive in sootup_handler.go.
.PHONY: build-sootup-jar-embed
build-sootup-jar-embed: build-sootup-jar
cp build/sootup/SootUpWrapper/target/sootup-wrapper-1.0.0.jar \
internal/callgraph/language/java/embedded/SootUpWrapper.jar
@echo "SootUpWrapper.jar embedded at internal/callgraph/language/java/embedded/SootUpWrapper.jar"

.PHONY: test-e2e-docker
docker-build-dev:
docker build -f build/docker/alpine.Dockerfile -t debricked/cli:dev --target dev .
Expand Down
90 changes: 90 additions & 0 deletions build/sootup/SootUpWrapper/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.debricked.sootup</groupId>
<artifactId>sootup-wrapper</artifactId>
<version>1.0.0</version>
<name>SootUpWrapper</name>
<description>
Callgraph generator using SootUp — replaces SootWrapper (classic Soot).
Accepts the same CLI interface as SootWrapper.jar for drop-in comparison.
Algorithms: CHA (default) or RTA, selectable via -a flag.
</description>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sootup.version>1.3.0</sootup.version>
</properties>
<dependencies>
<!-- SootUp core -->
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.core</artifactId>
<version>${sootup.version}</version>
</dependency>
<!-- SootUp Java language support -->
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.core</artifactId>
<version>${sootup.version}</version>
</dependency>
<!-- SootUp Java bytecode frontend (reads .class files and JARs) -->
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.bytecode</artifactId>
<version>${sootup.version}</version>
</dependency>
<!-- SootUp callgraph algorithms (CHA, RTA) -->
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.callgraph</artifactId>
<version>${sootup.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<!-- Build a fat (uber) JAR so it can be executed standalone -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.debricked.sootup.SootUpWrapper</mainClass>
</transformer>
<!-- Merge service files for SootUp's SPI usage -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#Generated by Maven
#Thu May 14 05:01:01 UTC 2026
groupId=com.debricked.sootup
artifactId=sootup-wrapper
version=1.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
com/debricked/sootup/SootUpWrapper$Args.class
com/debricked/sootup/SootUpWrapper$CallerEntry.class
com/debricked/sootup/SootUpWrapper.class
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/home/dritthi/projects/debricked-projects/cli/examples/callgraph/java-maven-sootup/SootUpWrapper/src/main/java/com/debricked/sootup/SootUpWrapper.java
Binary file not shown.
Binary file not shown.
Binary file not shown.
22 changes: 18 additions & 4 deletions internal/callgraph/language/java/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"os/exec"
"path"
"strings"
"syscall"

"github.com/debricked/cli/internal/callgraph/cgexec"
Expand All @@ -14,10 +15,11 @@ import (
)

const (
maven = "maven"
gradle = "gradle"
dependencyDir = ".debrickedTmpFolder"
outputName = "debricked-call-graph.java"
maven = "maven"
gradle = "gradle"
dependencyDir = ".debrickedTmpFolder"
outputName = "debricked-call-graph.java"
outputNameSootUp = "debricked-call-graph-sootup.java"
)

type Job struct {
Expand Down Expand Up @@ -55,6 +57,7 @@ func NewJob(
func (j *Job) Run() {
workingDirectory := j.GetDir()
pmConfig := j.config.PackageManager()
outputName := j.outputName()
targetDir := path.Join(workingDirectory, dependencyDir)
targetClasses := []string{workingDirectory}
if len(j.GetFiles()) > 0 {
Expand Down Expand Up @@ -123,6 +126,7 @@ func (j *Job) runCallGraph(callgraph ICallgraph) {

func (j *Job) runPostProcess() {
workingDirectory := j.GetDir()
outputName := j.outputName()
outputFullPath := path.Join(workingDirectory, outputName)
outputFullPathZip := outputFullPath + ".zip"
j.SendStatus("zipping callgraph")
Expand Down Expand Up @@ -154,3 +158,13 @@ func (j *Job) runPostProcess() {
}
}
}

func (j *Job) outputName() string {
if j.config != nil {
if engine, ok := j.config.Kwargs()["java-callgraph-engine"]; ok && strings.EqualFold(strings.TrimSpace(engine), "sootup") {
return outputNameSootUp
}
}

return outputName
}
12 changes: 12 additions & 0 deletions internal/callgraph/language/java/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ func TestNewJob(t *testing.T) {
assert.False(t, j.Errors().HasError())
}

func TestOutputNameDefault(t *testing.T) {
config := conf.NewConfig("java", nil, map[string]string{}, true, "maven", "")
j := NewJob(dir, files, testdata.NewEchoCmdFactory(), io.FileWriter{}, io.NewArchive("dir"), config, nil, io.FileSystem{}, testdata.MockSootHandler{})
assert.Equal(t, "debricked-call-graph.java", j.outputName())
}

func TestOutputNameSootUp(t *testing.T) {
config := conf.NewConfig("java", nil, map[string]string{"java-callgraph-engine": "sootup"}, true, "maven", "")
j := NewJob(dir, files, testdata.NewEchoCmdFactory(), io.FileWriter{}, io.NewArchive("dir"), config, nil, io.FileSystem{}, testdata.MockSootHandler{})
assert.Equal(t, "debricked-call-graph-sootup.java", j.outputName())
}

func TestRunMakeMavenCopyDependenciesCmdErr(t *testing.T) {
cmdErr := errors.New("cmd-error")
cmdFactoryMock := testdata.NewEchoCmdFactory()
Expand Down
14 changes: 9 additions & 5 deletions internal/callgraph/language/java/soot_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,24 @@ func (sh SootHandler) GetSootWrapper(version string, fs ioFs.IFileSystem, arc io
return "", err
}
}
path, err := filepath.Abs(path.Join(debrickedDir, "soot-wrapper.jar"))
absDebrickedDir, err := filepath.Abs(debrickedDir)
if err != nil {
return "", err
}
jarPath, err := filepath.Abs(path.Join(debrickedDir, "soot-wrapper.jar"))
if err != nil {

return "", err
}
if _, err := fs.Stat(path); fs.IsNotExist(err) {
if _, err := fs.Stat(jarPath); fs.IsNotExist(err) {
if version == "21" {
return sh.initializeSootWrapper(fs, debrickedDir)
return sh.initializeSootWrapper(fs, absDebrickedDir)
}

return path, sh.downloadSootWrapper(arc, fs, path, version)
return jarPath, sh.downloadSootWrapper(arc, fs, jarPath, version)
}

return path, nil
return jarPath, nil
}

func (sh SootHandler) getSootHandlerJavaVersion(version int) (string, error) {
Expand Down
58 changes: 58 additions & 0 deletions internal/callgraph/language/java/soot_handler_component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package java

import (
"fmt"
"path/filepath"
"strings"
"testing"

ioTestData "github.com/debricked/cli/internal/io/testdata"
"github.com/stretchr/testify/assert"
)

func TestSootHandlerComponent_GetSootWrapperInvalidVersionString(t *testing.T) {
h := SootHandler{}
_, err := h.GetSootWrapper("not-a-number", ioTestData.FileSystemMock{}, ioTestData.ArchiveMock{})
assert.EqualError(t, err, "could not convert version to int")
}

func TestSootHandlerComponent_GetSootWrapperUnsupportedJavaVersion(t *testing.T) {
h := SootHandler{}
_, err := h.GetSootWrapper("8", ioTestData.FileSystemMock{}, ioTestData.ArchiveMock{})
assert.EqualError(t, err, "lowest supported version for running callgraph generation is 11")
}

func TestSootHandlerComponent_GetSootWrapperMkdirError(t *testing.T) {
h := SootHandler{}
fsMock := ioTestData.FileSystemMock{
StatError: fmt.Errorf("missing"),
IsNotExistBool: true,
MkdirError: fmt.Errorf("mkdir failed"),
}

_, err := h.GetSootWrapper("11", fsMock, ioTestData.ArchiveMock{})
assert.EqualError(t, err, "mkdir failed")
}

func TestSootHandlerComponent_GetSootWrapperVersion21UsesEmbeddedJar(t *testing.T) {
h := SootHandler{}
fsMock := ioTestData.FileSystemMock{
StatError: fmt.Errorf("missing"),
IsNotExistBool: true,
}

p, err := h.GetSootWrapper("21", fsMock, ioTestData.ArchiveMock{})
assert.NoError(t, err)
assert.NotEmpty(t, p)
assert.True(t, strings.HasSuffix(filepath.ToSlash(p), "/.debricked/SootWrapper.jar"))
}

func TestSootHandlerComponent_GetSootWrapperReturnsExistingPath(t *testing.T) {
h := SootHandler{}
fsMock := ioTestData.FileSystemMock{}

p, err := h.GetSootWrapper("17", fsMock, ioTestData.ArchiveMock{})
assert.NoError(t, err)
assert.True(t, strings.HasSuffix(filepath.ToSlash(p), "/.debricked/soot-wrapper.jar"))
}

Loading