mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 15:37:50 -08:00
Signed-off-by: knqyf263 <knqyf263@gmail.com> Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
329 lines
8.2 KiB
Go
329 lines
8.2 KiB
Go
package jar_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar/sonatype"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar"
|
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
|
)
|
|
|
|
var (
|
|
// cd testdata/testimage/maven && docker build -t test .
|
|
// docker run --rm --name test -it test bash
|
|
// mvn dependency:list
|
|
// mvn dependency:tree -Dscope=compile -Dscope=runtime | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 1 { print }' | head -n -2 | awk '{print $NF}' | awk -F":" '{printf("{\""$1":"$2"\", \""$4 "\", \"\"},\n")}'
|
|
// paths filled in manually
|
|
wantMaven = []ftypes.Package{
|
|
{
|
|
Name: "com.example:web-app",
|
|
Version: "1.0-SNAPSHOT",
|
|
FilePath: "testdata/maven.war",
|
|
},
|
|
{
|
|
Name: "com.fasterxml.jackson.core:jackson-databind",
|
|
Version: "2.9.10.6",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/jackson-databind-2.9.10.6.jar",
|
|
},
|
|
{
|
|
Name: "com.fasterxml.jackson.core:jackson-annotations",
|
|
Version: "2.9.10",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/jackson-annotations-2.9.10.jar",
|
|
},
|
|
{
|
|
Name: "com.fasterxml.jackson.core:jackson-core",
|
|
Version: "2.9.10",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/jackson-core-2.9.10.jar",
|
|
},
|
|
{
|
|
Name: "com.cronutils:cron-utils",
|
|
Version: "9.1.2",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/cron-utils-9.1.2.jar",
|
|
},
|
|
{
|
|
Name: "org.slf4j:slf4j-api",
|
|
Version: "1.7.30",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/slf4j-api-1.7.30.jar",
|
|
},
|
|
{
|
|
Name: "org.glassfish:javax.el",
|
|
Version: "3.0.0",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/javax.el-3.0.0.jar",
|
|
},
|
|
{
|
|
Name: "org.apache.commons:commons-lang3",
|
|
Version: "3.11",
|
|
FilePath: "testdata/maven.war/WEB-INF/lib/commons-lang3-3.11.jar",
|
|
},
|
|
}
|
|
|
|
// cd testdata/testimage/gradle && docker build -t test .
|
|
// docker run --rm --name test -it test bash
|
|
// gradle app:dependencies --configuration implementation | grep "[+\]---" | cut -d" " -f2 | awk -F":" '{printf("{\""$1":"$2"\", \""$3"\", \"\"},\n")}'
|
|
// paths filled in manually
|
|
wantGradle = []ftypes.Package{
|
|
{
|
|
Name: "commons-dbcp:commons-dbcp",
|
|
Version: "1.4",
|
|
FilePath: "testdata/gradle.war/WEB-INF/lib/commons-dbcp-1.4.jar",
|
|
},
|
|
{
|
|
Name: "commons-pool:commons-pool",
|
|
Version: "1.6",
|
|
FilePath: "testdata/gradle.war/WEB-INF/lib/commons-pool-1.6.jar",
|
|
},
|
|
{
|
|
Name: "log4j:log4j",
|
|
Version: "1.2.17",
|
|
FilePath: "testdata/gradle.war/WEB-INF/lib/log4j-1.2.17.jar",
|
|
},
|
|
{
|
|
Name: "org.apache.commons:commons-compress",
|
|
Version: "1.19",
|
|
FilePath: "testdata/gradle.war/WEB-INF/lib/commons-compress-1.19.jar",
|
|
},
|
|
}
|
|
|
|
// manually created
|
|
wantSHA1 = []ftypes.Package{
|
|
{
|
|
Name: "org.springframework:spring-core",
|
|
Version: "5.3.3",
|
|
FilePath: "testdata/test.jar",
|
|
},
|
|
}
|
|
|
|
// offline
|
|
wantOffline = []ftypes.Package{
|
|
{
|
|
Name: "org.springframework:Spring Framework",
|
|
Version: "2.5.6.SEC03",
|
|
FilePath: "testdata/test.jar",
|
|
},
|
|
}
|
|
|
|
// manually created
|
|
wantHeuristic = []ftypes.Package{
|
|
{
|
|
Name: "com.example:heuristic",
|
|
Version: "1.0.0-SNAPSHOT",
|
|
FilePath: "testdata/heuristic-1.0.0-SNAPSHOT.jar",
|
|
},
|
|
}
|
|
|
|
// manually created
|
|
wantFatjar = []ftypes.Package{
|
|
{
|
|
Name: "com.google.guava:failureaccess",
|
|
Version: "1.0.1",
|
|
FilePath: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
},
|
|
{
|
|
Name: "com.google.guava:guava",
|
|
Version: "29.0-jre",
|
|
FilePath: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
},
|
|
{
|
|
Name: "com.google.guava:listenablefuture",
|
|
Version: "9999.0-empty-to-avoid-conflict-with-guava",
|
|
FilePath: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
},
|
|
{
|
|
Name: "com.google.j2objc:j2objc-annotations",
|
|
Version: "1.3",
|
|
FilePath: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
},
|
|
{
|
|
Name: "org.apache.hadoop.thirdparty:hadoop-shaded-guava",
|
|
Version: "1.1.0-SNAPSHOT",
|
|
FilePath: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
},
|
|
}
|
|
|
|
// manually created
|
|
wantNestedJar = []ftypes.Package{
|
|
{
|
|
Name: "test:nested",
|
|
Version: "0.0.1",
|
|
FilePath: "testdata/nested.jar",
|
|
},
|
|
{
|
|
Name: "test:nested2",
|
|
Version: "0.0.2",
|
|
FilePath: "testdata/nested.jar/META-INF/jars/nested2.jar",
|
|
},
|
|
{
|
|
Name: "test:nested3",
|
|
Version: "0.0.3",
|
|
FilePath: "testdata/nested.jar/META-INF/jars/nested2.jar/META-INF/jars/nested3.jar",
|
|
},
|
|
}
|
|
|
|
// manually created
|
|
wantDuplicatesJar = []ftypes.Package{
|
|
{
|
|
Name: "io.quarkus.gizmo:gizmo",
|
|
Version: "1.1.1.Final",
|
|
FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar",
|
|
},
|
|
{
|
|
Name: "log4j:log4j",
|
|
Version: "1.2.16",
|
|
FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar/jars/log4j-1.2.16.jar",
|
|
},
|
|
{
|
|
Name: "log4j:log4j",
|
|
Version: "1.2.17",
|
|
FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar/jars/log4j-1.2.17.jar",
|
|
},
|
|
}
|
|
)
|
|
|
|
type apiResponse struct {
|
|
Response response `json:"response"`
|
|
}
|
|
|
|
type response struct {
|
|
NumFound int `json:"numFound"`
|
|
Docs []doc `json:"docs"`
|
|
}
|
|
|
|
type doc struct {
|
|
ID string `json:"id"`
|
|
GroupID string `json:"g"`
|
|
ArtifactID string `json:"a"`
|
|
Version string `json:"v"`
|
|
P string `json:"p"`
|
|
VersionCount int `json:versionCount`
|
|
}
|
|
|
|
func TestParse(t *testing.T) {
|
|
vectors := []struct {
|
|
name string
|
|
file string // Test input file
|
|
offline bool
|
|
want []ftypes.Package
|
|
}{
|
|
{
|
|
name: "maven",
|
|
file: "testdata/maven.war",
|
|
want: wantMaven,
|
|
},
|
|
{
|
|
name: "gradle",
|
|
file: "testdata/gradle.war",
|
|
want: wantGradle,
|
|
},
|
|
{
|
|
name: "nested jars",
|
|
file: "testdata/nested.jar",
|
|
want: wantNestedJar,
|
|
},
|
|
{
|
|
name: "sha1 search",
|
|
file: "testdata/test.jar",
|
|
want: wantSHA1,
|
|
},
|
|
{
|
|
name: "offline",
|
|
file: "testdata/test.jar",
|
|
offline: true,
|
|
want: wantOffline,
|
|
},
|
|
{
|
|
name: "artifactId search",
|
|
file: "testdata/heuristic-1.0.0-SNAPSHOT.jar",
|
|
want: wantHeuristic,
|
|
},
|
|
{
|
|
name: "fat jar",
|
|
file: "testdata/hadoop-shaded-guava-1.1.0-SNAPSHOT.jar",
|
|
want: wantFatjar,
|
|
},
|
|
{
|
|
name: "duplicate libraries",
|
|
file: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar",
|
|
want: wantDuplicatesJar,
|
|
},
|
|
}
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
res := apiResponse{
|
|
Response: response{
|
|
NumFound: 1,
|
|
},
|
|
}
|
|
|
|
switch {
|
|
case strings.Contains(r.URL.Query().Get("q"), "springframework"):
|
|
res.Response.NumFound = 0
|
|
case strings.Contains(r.URL.Query().Get("q"), "c666f5bc47eb64ed3bbd13505a26f58be71f33f0"):
|
|
res.Response.Docs = []doc{
|
|
{
|
|
ID: "org.springframework.spring-core",
|
|
GroupID: "org.springframework",
|
|
ArtifactID: "spring-core",
|
|
Version: "5.3.3",
|
|
},
|
|
}
|
|
case strings.Contains(r.URL.Query().Get("q"), "Gizmo"):
|
|
res.Response.NumFound = 0
|
|
case strings.Contains(r.URL.Query().Get("q"), "85d30c06026afd9f5be26da3194d4698c447a904"):
|
|
res.Response.Docs = []doc{
|
|
{
|
|
ID: "io.quarkus.gizmo.gizmo",
|
|
GroupID: "io.quarkus.gizmo",
|
|
ArtifactID: "gizmo",
|
|
Version: "1.1.1.Final",
|
|
},
|
|
}
|
|
case strings.Contains(r.URL.Query().Get("q"), "heuristic"):
|
|
res.Response.Docs = []doc{
|
|
{
|
|
ID: "org.springframework.heuristic",
|
|
GroupID: "org.springframework",
|
|
ArtifactID: "heuristic",
|
|
VersionCount: 10,
|
|
},
|
|
{
|
|
ID: "com.example.heuristic",
|
|
GroupID: "com.example",
|
|
ArtifactID: "heuristic",
|
|
VersionCount: 100,
|
|
},
|
|
}
|
|
}
|
|
_ = json.NewEncoder(w).Encode(res)
|
|
}))
|
|
|
|
for _, v := range vectors {
|
|
t.Run(v.name, func(t *testing.T) {
|
|
f, err := os.Open(v.file)
|
|
require.NoError(t, err)
|
|
|
|
stat, err := f.Stat()
|
|
require.NoError(t, err)
|
|
|
|
c := sonatype.New(sonatype.WithURL(ts.URL), sonatype.WithHTTPClient(ts.Client()))
|
|
p := jar.NewParser(c, jar.WithFilePath(v.file), jar.WithOffline(v.offline), jar.WithSize(stat.Size()))
|
|
|
|
got, _, err := p.Parse(f)
|
|
require.NoError(t, err)
|
|
|
|
sort.Sort(ftypes.Packages(got))
|
|
sort.Sort(ftypes.Packages(v.want))
|
|
|
|
assert.Equal(t, v.want, got)
|
|
})
|
|
}
|
|
}
|