github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/ant/ant.go (about) 1 package ant 2 3 import ( 4 "archive/zip" 5 "bufio" 6 "encoding/xml" 7 "errors" 8 "os" 9 "path/filepath" 10 "regexp" 11 "strings" 12 13 "github.com/apex/log" 14 "github.com/bmatcuk/doublestar" 15 "github.com/gnewton/jargo" 16 17 "github.com/fossas/fossa-cli/buildtools/maven" 18 "github.com/fossas/fossa-cli/graph" 19 "github.com/fossas/fossa-cli/pkg" 20 ) 21 22 func Graph(dir string) (graph.Deps, error) { 23 jarFilePaths, err := doublestar.Glob(filepath.Join(dir, "*.jar")) 24 if err != nil { 25 return graph.Deps{}, err 26 } 27 28 log.Debugf("Running Ant analysis: %#v", jarFilePaths) 29 30 // traverse through libdir and and resolve jars 31 var imports []pkg.Import 32 depGraph := make(map[pkg.ID]pkg.Package) 33 for _, jarFilePath := range jarFilePaths { 34 locator, err := locatorFromJar(jarFilePath) 35 if err == nil { 36 imports = append(imports, pkg.Import{ 37 Resolved: locator, 38 }) 39 depGraph[locator] = pkg.Package{ 40 ID: locator, 41 } 42 } else { 43 log.Warnf("unable to resolve Jar: %s", jarFilePath) 44 } 45 } 46 47 return graph.Deps{ 48 Direct: imports, 49 Transitive: depGraph, 50 }, nil 51 } 52 53 // locatorFromJar resolves a locator from a .jar file by inspecting its contents. 54 func locatorFromJar(path string) (pkg.ID, error) { 55 log.Debugf("processing locator from Jar: %s", path) 56 57 info, err := jargo.GetJarInfo(path) 58 if err == nil { 59 // first, attempt to resolve a pomfile from the META-INF directory 60 var pomFilePath string 61 for _, file := range info.Files { 62 if strings.HasPrefix(file, "META-INF") && strings.HasSuffix(file, "pom.xml") && (pomFilePath == "" || len(pomFilePath) > len(file)) { 63 pomFilePath = file 64 } 65 } 66 67 pomFile, err := getPOMFromJar(pomFilePath) 68 if err == nil { 69 log.Debugf("resolving locator from pom: %s", pomFilePath) 70 return pkg.ID{ 71 Type: pkg.Maven, 72 Name: pomFile.GroupID + ":" + pomFile.ArtifactID, 73 Revision: pomFile.Version, 74 }, nil 75 } else { 76 log.Debugf("%s", err) 77 } 78 79 // failed to decode pom file, fall back to META-INF 80 manifest := *info.Manifest 81 if manifest["Bundle-SymbolicName"] != "" && manifest["Implementation-Version"] != "" { 82 log.Debugf("resolving locator from META-INF: %s", info.Manifest) 83 return pkg.ID{ 84 Type: pkg.Maven, 85 Name: manifest["Bundle-SymbolicName"], // TODO: identify GroupId 86 Revision: manifest["Implementation-Version"], 87 }, nil 88 } 89 } 90 91 // fall back to parsing file name 92 re := regexp.MustCompile("(-sources|-javadoc)?.jar$") 93 nameParts := strings.Split(re.ReplaceAllString(filepath.Base(path), ""), "-") 94 lenNameParts := len(nameParts) 95 96 var parsedProjectName string 97 var parsedRevisionName string 98 99 if lenNameParts == 1 { 100 parsedProjectName = nameParts[0] 101 } else if lenNameParts > 1 { 102 parsedProjectName = strings.Join(nameParts[0:lenNameParts-1], "-") 103 parsedRevisionName = nameParts[lenNameParts-1] 104 } 105 106 if parsedProjectName == "" { 107 return pkg.ID{}, errors.New("unable to parse jar file") 108 } 109 110 return pkg.ID{ 111 Type: pkg.Maven, 112 Name: parsedProjectName, 113 Revision: parsedRevisionName, 114 }, nil 115 } 116 117 func getPOMFromJar(path string) (maven.Manifest, error) { 118 var pomFile maven.Manifest 119 120 log.Debugf(path) 121 if path == "" { 122 return pomFile, errors.New("invalid POM path specified") 123 } 124 125 jarFile, err := os.Open(path) 126 if err != nil { 127 return pomFile, err 128 } 129 130 defer jarFile.Close() 131 132 zfi, err := jarFile.Stat() 133 if err != nil { 134 return pomFile, err 135 } 136 137 zr, err := zip.NewReader(jarFile, zfi.Size()) 138 if err != nil { 139 return pomFile, err 140 } 141 142 for _, f := range zr.File { 143 // decode a single pom.xml directly from jar 144 if f.Name == path { 145 rc, err := f.Open() 146 if err != nil { 147 return pomFile, err 148 } 149 defer rc.Close() 150 151 reader := bufio.NewReader(rc) 152 decoder := xml.NewDecoder(reader) 153 154 if err := decoder.Decode(&pomFile); err != nil { 155 return pomFile, err 156 } 157 158 return pomFile, nil 159 } 160 } 161 162 return pomFile, errors.New("unable to parse POM from Jar") 163 }