diff --git a/pom.xml b/pom.xml index 2267c88..f45f269 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.phsyberdome phsyberdome-sca-cli - 1.0.2-beta + 1.0.3-beta jar diff --git a/src/main/java/com/phsyberdome/drona/Plugins/JavaMavenPlugin.java b/src/main/java/com/phsyberdome/drona/Plugins/JavaMavenPlugin.java index 599037b..a4c4f5c 100644 --- a/src/main/java/com/phsyberdome/drona/Plugins/JavaMavenPlugin.java +++ b/src/main/java/com/phsyberdome/drona/Plugins/JavaMavenPlugin.java @@ -11,6 +11,7 @@ import com.phsyberdome.drona.Models.Pair; import com.phsyberdome.drona.Utils.FileUtil; import com.phsyberdome.drona.Utils.MavenRepoHelper; +import com.phsyberdome.drona.Utils.MavenVersionHelper; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; @@ -18,6 +19,9 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; @@ -41,9 +45,13 @@ public class JavaMavenPlugin implements PluginInterface private ArrayList modules; private final LicenseDetector licenseDetector; + + private Set scannedDependencies; + public JavaMavenPlugin(LicenseDetector licenseDetector) { this.licenseDetector = licenseDetector; + this.scannedDependencies = new HashSet<>(); } @@ -77,7 +85,6 @@ public void readModules() { String rootGroupId = MavenRepoHelper.extractAttributeFromNode(doc.getDocumentElement(), "groupId"); String rootVersion = MavenRepoHelper.extractAttributeFromNode(doc.getDocumentElement(), "version"); Module root = new Module(rootArtifactId,rootVersion); - NodeList list = doc.getElementsByTagName("dependency"); for(int i=0;i getModules() { return modules; } + + private boolean alreadyScanned(Module m){ + Iterator it = scannedDependencies.iterator(); + while(it.hasNext()){ + Module a = (Module) it.next(); + if(a.getName()==m.getName() && a.getVersion() == m.getVersion()){ + return true; + } + } + return false; + } + + private Module getScannedModule(Module m){ + Iterator it = scannedDependencies.iterator(); + while(it.hasNext()){ + Module a = (Module) it.next(); + if(a.getName().strip().equalsIgnoreCase(m.getName().strip()) && a.getVersion().strip().equalsIgnoreCase(m.getVersion().strip())){ + return a; + } + } + return m; + } + private Module getScannedModule(String artifactId){ + Iterator it = scannedDependencies.iterator(); + while(it.hasNext()){ + Module a = (Module) it.next(); + if(a.getName().strip().equalsIgnoreCase(artifactId.strip())){ + return a; + } + } + return new Module(artifactId,""); + } + + private boolean isAlreadyScannedArtifact(String artifiactId) { + Iterator it = scannedDependencies.iterator(); + while(it.hasNext()){ + Module a = (Module) it.next(); + if(a.getName().equalsIgnoreCase(artifiactId)){ + return true; + } + } + return false; + } } diff --git a/src/main/java/com/phsyberdome/drona/Utils/FileUtil.java b/src/main/java/com/phsyberdome/drona/Utils/FileUtil.java index 4b8180f..ee789fb 100644 --- a/src/main/java/com/phsyberdome/drona/Utils/FileUtil.java +++ b/src/main/java/com/phsyberdome/drona/Utils/FileUtil.java @@ -156,7 +156,7 @@ public static Path getFilePathFromURL(String path, String cloneLocation){ return null; } - public static File downloadFile(String path, String url){ + public static File downloadFile(String path, String url)throws IOException{ File file = FileSystems.getDefault().getPath(path).toFile(); if(file.exists()) { FileUtil.deleteDirectory(file); @@ -164,11 +164,8 @@ public static File downloadFile(String path, String url){ try { FileUtils.copyURLToFile(new URL(url), file); } catch (MalformedURLException ex) { - Logger.getLogger(MavenRepoHelper.class.getName()).log(Level.SEVERE, null, ex); +// Logger.getLogger(MavenRepoHelper.class.getName()).log(Level.SEVERE, null, ex); return null; - } catch (IOException ex) { - Logger.getLogger(MavenRepoHelper.class.getName()).log(Level.SEVERE, null, ex); - return null; } return file; } @@ -218,10 +215,10 @@ public static String readFile(Path filePath) { text = content.collect(Collectors.joining("\n")); } return text; - } catch (IOException ex) { + } catch (Exception ex) { CLIHelper.updateCurrentLine(ex.getLocalizedMessage(), Ansi.Color.RED); - return null; } + return ""; } public static void extractZipFolder(String zipFile,String extractFolder) @@ -334,7 +331,7 @@ public static void extractTarball(String tarfile,String toPath) throws FileNotFo tarEntry = tarIn.getNextTarEntry(); } tarIn.close(); - }catch(IOException e){ + }catch(Exception e){ CLIHelper.updateCurrentLine("Failed to untar file " + tarfile,Ansi.Color.RED); } diff --git a/src/main/java/com/phsyberdome/drona/Utils/JSONHelper.java b/src/main/java/com/phsyberdome/drona/Utils/JSONHelper.java index 416a3f9..a4aa03a 100644 --- a/src/main/java/com/phsyberdome/drona/Utils/JSONHelper.java +++ b/src/main/java/com/phsyberdome/drona/Utils/JSONHelper.java @@ -5,15 +5,14 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.phsyberdome.drona.CLIHelper; import com.phsyberdome.drona.Models.Pair; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; +import org.fusesource.jansi.Ansi; /** * @@ -27,7 +26,7 @@ public static String convertToJson(Object object) { try { json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); }catch(JsonProcessingException e) { - Logger.getLogger(JSONHelper.class.getCanonicalName()).log(Level.WARNING, e.getLocalizedMessage()); + CLIHelper.updateCurrentLine("Failed to convert to json!", Ansi.Color.RED); return null; } return json; @@ -38,7 +37,7 @@ public static T convertToObj(Class c,String json) { ObjectMapper objectMapper = new ObjectMapper(); return (T) objectMapper.readValue(json, c); }catch(Exception e) { - Logger.getLogger(JSONHelper.class.getCanonicalName()).log(Level.WARNING, e.getLocalizedMessage()); + CLIHelper.updateCurrentLine("Failed to parse json!", Ansi.Color.RED); } return null; } @@ -49,8 +48,8 @@ public static String getValue(String keyPath,String json){ try { root = objectMapper.readTree(json); return root.at(keyPath).asText(); - } catch (JsonProcessingException ex) { - Logger.getLogger(JSONHelper.class.getName()).log(Level.SEVERE, null, ex); + } catch (Exception e){ + CLIHelper.updateCurrentLine("Failed to parse json!", Ansi.Color.RED); } return null; } @@ -68,8 +67,8 @@ public static List getValues(String keyPath,String json){ res.add(field.getKey()); } return res; - } catch (JsonProcessingException ex) { - Logger.getLogger(JSONHelper.class.getName()).log(Level.SEVERE, null, ex); + } catch (Exception ex) { + CLIHelper.updateCurrentLine("Failed to parse json!", Ansi.Color.RED); } return null; } diff --git a/src/main/java/com/phsyberdome/drona/Utils/MavenRepoHelper.java b/src/main/java/com/phsyberdome/drona/Utils/MavenRepoHelper.java index dd59ec2..e251c21 100644 --- a/src/main/java/com/phsyberdome/drona/Utils/MavenRepoHelper.java +++ b/src/main/java/com/phsyberdome/drona/Utils/MavenRepoHelper.java @@ -4,6 +4,7 @@ import com.phsyberdome.drona.CLIHelper; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -61,11 +62,19 @@ public static Document readXMLDocument(Path path) { - public static Document getMavenMetadataDocument(String groupId, String artifactId) { + public static Document getMavenMetadataDocument(String groupId, String artifactId){ String repoUrl = buildRootRepoUrl(groupId, artifactId); String mvnMetadataFileUrl = repoUrl + "maven-metadata.xml"; - File metadataFile = FileUtil.downloadFile("/.drona/temp/data/metadata.xml", mvnMetadataFileUrl); + File metadataFile; + try { + metadataFile = FileUtil.downloadFile("/.drona/temp/data/metadata.xml", mvnMetadataFileUrl); + } catch (IOException ex) { + return null; + } + if(metadataFile==null) { + return null; + } Document doc = readXMLDocument(metadataFile.toPath()); if(metadataFile.exists()){ FileUtil.deleteDirectory(metadataFile); @@ -135,7 +144,6 @@ public static String getVersionFromParent(String artifactId,Document doc){ public static Document getParentPOM(Document pom) { NodeList parents = pom.getElementsByTagName("parent"); if(parents.getLength() <= 0){ - System.out.println("This is the root pom!"); return null; } Element parent = (Element) parents.item(0); @@ -144,7 +152,12 @@ public static Document getParentPOM(Document pom) { String version = parent.getElementsByTagName("version").item(0).getTextContent(); //version = resolvePropertyValue(version, pom); String urlToParentPom = buildUrlForPomFile(groupId, artifactId, version); - File file = FileUtil.downloadFile("/.drona/temp/poms/pom.xml", urlToParentPom); + File file; + try { + file = FileUtil.downloadFile("/.drona/temp/poms/"+artifactId+"_parent_pom.xml", urlToParentPom); + } catch (IOException ex) { + return null; + } Document doc = readXMLDocument(file.toPath()); if(file.exists()){ FileUtil.deleteDirectory(file); diff --git a/src/main/java/com/phsyberdome/drona/Utils/MavenVersion.java b/src/main/java/com/phsyberdome/drona/Utils/MavenVersion.java new file mode 100644 index 0000000..4d78f9f --- /dev/null +++ b/src/main/java/com/phsyberdome/drona/Utils/MavenVersion.java @@ -0,0 +1,52 @@ + + +package com.phsyberdome.drona.Utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * + * @author Pratham Gahlout + */ + + +public class MavenVersion { + + private List ranges; + + public MavenVersion(MavenVersionRange... ranges) { + this.ranges = new ArrayList<>(); + for(var range:ranges) { + this.ranges.add(range); + } + } + + public MavenVersion(List ranges) { + this.ranges = ranges; + } + + public boolean isSatisfied(String version) { + boolean isSatisfied = false; + + for(var range:ranges) { + isSatisfied |= range.isSatisfied(version); + } + + return isSatisfied; + } + + @Override + public String toString() { + return ranges.stream() + .map(MavenVersionRange::toString) + .collect(Collectors.joining("")); + } + + + + +} diff --git a/src/main/java/com/phsyberdome/drona/Utils/MavenVersionHelper.java b/src/main/java/com/phsyberdome/drona/Utils/MavenVersionHelper.java index 690f56d..1480b99 100644 --- a/src/main/java/com/phsyberdome/drona/Utils/MavenVersionHelper.java +++ b/src/main/java/com/phsyberdome/drona/Utils/MavenVersionHelper.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nullable; import org.fusesource.jansi.Ansi; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -23,13 +24,16 @@ */ public class MavenVersionHelper { - public static String softVersionRegex = "(?[\\d.\\D]+)"; - public static String hardVersionRegex = "(?:^\\[(?[\\d.\\D]+)\\]$)"; - public static String conditionalVersionRegex = "(?:(?(?<=\\]|\\)),(?=\\[|\\())|,|(?:(?<=,)(?:(?[\\d.\\D]+)\\]|(?[\\d.]+)\\)))|(?:(?:\\[(?[\\d.]+)|\\((?[\\d.]+))(?=,)))+"; +// public static String softVersionRegex = "(?[\\d.\\D]+)"; +// public static String hardVersionRegex = "(?:^\\[(?[\\d.\\D]+)\\]$)"; +// public static String conditionalVersionRegex = "(?:(?(?<=\\]|\\)),(?=\\[|\\())|,|(?:(?<=,)(?:(?[\\d.\\D]+)\\]|(?[\\d.]+)\\)))|(?:(?:\\[(?[\\d.]+)|\\((?[\\d.]+))(?=,)))+"; public static List getAllVersions(String groupId,String artifactId) { - Document document = MavenRepoHelper.getMavenMetadataDocument(groupId, artifactId); + @Nullable Document document = MavenRepoHelper.getMavenMetadataDocument(groupId, artifactId); + if(document == null) { + return new ArrayList<>(); + } NodeList list = document.getElementsByTagName("version"); List versions = new ArrayList(); for(int i=0;i getAllVersions(String groupId,String artifactId) { } public static String resolveVersion(String groupId,String artifactId,String version) { + if(version.length() == 0 || isPureVersion(version)) { + return version; + } ArrayList allVersions = (ArrayList) getAllVersions(groupId, artifactId); + if(allVersions.isEmpty()) { + return version; + } + List ranges = extractRanges(version); + MavenVersion versionSpec = new MavenVersion(ranges); + List satisfiedVersions = new ArrayList<>(); + try { + for(var v: allVersions) { + if(versionSpec.isSatisfied(v)){ + satisfiedVersions.add(v); + } + } + List semverVersions = new ArrayList<>(); + for(var s: satisfiedVersions) { + semverVersions.add(new Semver(s,Semver.SemverType.NPM)); + } + Collections.sort(semverVersions); + return !semverVersions.isEmpty()? semverVersions.get(semverVersions.size()-1).toString() : version; + } catch (SemverException ex) { + return version; + } + } + + private static List extractRanges(String version) { + List ranges = new ArrayList<>(); + + Pattern combinationOfVersionPattern = Pattern.compile("(?[\\[(]([\\d\\W\\w]\\.?)*,([\\d\\W\\w]\\.?)*[\\])])(?,)(?[\\[(]([\\d\\W\\w]\\.?)*,(\\d\\.?)*[\\])])"); + Matcher matcher = combinationOfVersionPattern.matcher(version); try { - Pattern pattern = Pattern.compile(softVersionRegex+"|"+hardVersionRegex+"|"+conditionalVersionRegex,Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(version); - - if(matcher.find()){ - if(matcher.group("eq") != null){ - return matcher.group("eq"); - }else if(matcher.group("heq")!=null){ - return matcher.group("heq"); - }else if(matcher.group("lte")!=null){ - if(matcher.group("gte") !=null) { - }else { - allVersions.removeIf(s -> { - try { - Semver version1 = new Semver(s,Semver.SemverType.IVY); - Semver version2 = new Semver(matcher.group("lte"), Semver.SemverType.IVY); - return version1.isGreaterThan(version2); - }catch(SemverException e){ - System.out.println("not following semantic version "+s); - return false; - } - }); - return allVersions.get(allVersions.size()-1); - } - }else if(matcher.group("gte")!=null){ + if(matcher.matches()){ + matcher.reset(); + while(matcher.find()){ + String versionLeft = matcher.group("versionLeft"); + String versionRight = matcher.group("versionRight"); + // now this will cause duplication of mavenversionrange in the list, + // we can make it comparable and thus avoid adding the same range again, + // anyways we dont care right now because its not going to be a very long list + System.out.println(versionLeft + "," + versionRight); + ranges.add(MavenVersionRange.parse(versionLeft)); + ranges.add(MavenVersionRange.parse(versionRight)); } - - else if(matcher.group("gte")!=null && matcher.group("lt")!=null){ - - }else if(matcher.group("gt")!=null && matcher.group("lt")!=null){ - + if(ranges.isEmpty()){ + // Means no matches for combination of ranges + ranges.add(MavenVersionRange.parse(version)); + System.out.println(MavenVersionRange.parse(version)); } + }else { - CLIHelper.updateCurrentLine("Regex failed to match", Ansi.Color.RED); + ranges.add(MavenVersionRange.parse(version)); } - }catch(IllegalStateException e){ - return version; + } catch(Exception e) { + CLIHelper.updateCurrentLine("Failed to resolve version!", Ansi.Color.RED); } - return version; + + return ranges; } + private static boolean isPureVersion(String version) { + return !version.contains("(") + || !version.contains(")") + || !version.contains("]") + || !version.contains("[") + || !version.contains("."); + } } diff --git a/src/main/java/com/phsyberdome/drona/Utils/MavenVersionRange.java b/src/main/java/com/phsyberdome/drona/Utils/MavenVersionRange.java new file mode 100644 index 0000000..9e7ebd0 --- /dev/null +++ b/src/main/java/com/phsyberdome/drona/Utils/MavenVersionRange.java @@ -0,0 +1,114 @@ + + +package com.phsyberdome.drona.Utils; + +import com.vdurmont.semver4j.Semver; +import com.vdurmont.semver4j.SemverException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author Pratham Gahlout + */ +class MavenVersionRange { + private boolean isLeftClosedInterval = false; + private boolean isRightClosedInterval = false; + private String A; + private String B; + + private MavenVersionRange(String A, boolean isHard) { + this.A = A; + this.B = A; + isLeftClosedInterval = isHard; + isRightClosedInterval = isHard; + } + + private MavenVersionRange(String A, String B, boolean isLeftClosed, boolean isRightClosed) { + this.A = A; + this.B = B; + isLeftClosedInterval = isLeftClosed; + isRightClosedInterval = isRightClosed; + } + + + public static MavenVersionRange parse(String version) throws Exception { + Pattern pattern = Pattern.compile(","); + Matcher matcher = pattern.matcher(version); + + if(matcher.find()){ + // This is a range. This only supports a single range and not a combination of ranges + // so we assume the occurance of ',' will only be one time + Pattern rangeMatchPattern = Pattern.compile("[(\\[](?([\\d\\w\\W]\\.?)*),(?([\\d\\w\\W]\\.?)*)[\\])]"); + Matcher rangeMatch = rangeMatchPattern.matcher(version); + if(rangeMatch.matches()){ + var isLeftClosed = !version.contains("("); + var isRightClosed = !version.contains(")"); + var leftInterval = rangeMatch.group("left"); + var rightInterval = rangeMatch.group("right"); + leftInterval = leftInterval.isBlank() ? "0" : leftInterval; + rightInterval = rightInterval.isBlank() ? "999999" : rightInterval; + return new MavenVersionRange(leftInterval,rightInterval,isLeftClosed,isRightClosed); + }else { + // Throw invalid version exception + throw new Exception(); + } + + }else { + // Its either a soft requirement or a hard requirement + if(version.endsWith("]")){ + // its a hard requirement + return new MavenVersionRange(version.replaceAll("\\]","").replaceAll("\\[", ""), true); + }else { + // its a soft requirement + return new MavenVersionRange(version,false); + } + } + } + + public boolean isSatisfied(String version) throws SemverException{ + Semver leftInterval = new Semver(A, Semver.SemverType.NPM); + Semver rightInterval = new Semver(B, Semver.SemverType.NPM); + Semver versionToCompare = new Semver(version, Semver.SemverType.NPM); + if(leftInterval.isEqualTo(rightInterval)) { + return versionToCompare.isEqualTo(leftInterval); + }else { + if(isLeftClosedInterval && isRightClosedInterval) { + // Means a hard req + + return versionToCompare.isGreaterThanOrEqualTo(leftInterval) + && versionToCompare.isLowerThanOrEqualTo(rightInterval); + }else if(isLeftClosedInterval) { + return versionToCompare.isGreaterThanOrEqualTo(leftInterval) + && versionToCompare.isLowerThan(rightInterval); + }else if(isRightClosedInterval) { + return versionToCompare.isGreaterThan(leftInterval) + && versionToCompare.isLowerThanOrEqualTo(rightInterval); + }else { + return versionToCompare.isGreaterThan(leftInterval) + && versionToCompare.isLowerThan(rightInterval); + } + } + + } + + @Override + public String toString() { + String str = ""; + str += isLeftClosedInterval ? "[" : "("; + str += A + ","; + str += B; + str += isRightClosedInterval ? "]" : ")"; + return str; + } + + public boolean equals(MavenVersionRange obj) { + return this.isLeftClosedInterval == obj.isLeftClosedInterval + && this.isRightClosedInterval == obj.isRightClosedInterval + && this.A.equals(obj.A) + && this.B.equals(obj.B); + } + + + +} diff --git a/src/main/java/com/phsyberdome/drona/licensedetector/LicenseDatabase.java b/src/main/java/com/phsyberdome/drona/licensedetector/LicenseDatabase.java index 7d79e66..3e62a47 100644 --- a/src/main/java/com/phsyberdome/drona/licensedetector/LicenseDatabase.java +++ b/src/main/java/com/phsyberdome/drona/licensedetector/LicenseDatabase.java @@ -114,8 +114,10 @@ private void downloadLicenseListData(){ pathToLicenseListJSON = pathToLicenseList.toString(); } catch (MalformedURLException ex) { Logger.getLogger(LicenseDatabase.class.getName()).log(Level.SEVERE, null, ex); + System.exit(0); } catch (IOException ex) { Logger.getLogger(LicenseDatabase.class.getName()).log(Level.SEVERE, null, ex); + System.exit(0); } CLIHelper.printLine("License data downloaded at "+pathToLicenseListJSON.toString(), Color.GREEN); diff --git a/src/test/java/com/phsyberdome/drona/Utils/MavenVersionHelperTest.java b/src/test/java/com/phsyberdome/drona/Utils/MavenVersionHelperTest.java index 69866f8..92d3879 100644 --- a/src/test/java/com/phsyberdome/drona/Utils/MavenVersionHelperTest.java +++ b/src/test/java/com/phsyberdome/drona/Utils/MavenVersionHelperTest.java @@ -56,9 +56,9 @@ public void testResolveVersion() { System.out.println("resolveVersion"); String groupId = "com.fasterxml.jackson.core"; String artifactId = "jackson-databind"; -// String version = "(,2.4.0-rc4]"; - String version = "2.4.0-rc3"; - String expResult = "2.4.0-rc3"; + String version = "[2.16.0,2.17.0-rc1)"; +// String version = "2.4.0-rc3"; + String expResult = "2.16.1"; String result = MavenVersionHelper.resolveVersion(groupId,artifactId,version); assertEquals(expResult,result); }