From 7cd56211717ef65c0c41434789719cffda06ecf3 Mon Sep 17 00:00:00 2001 From: Steven Tracey Date: Thu, 17 Nov 2022 23:21:26 -0500 Subject: [PATCH] Cleaned up code, sanitized inputs --- build.gradle | 4 +- .../nevets/codebadgerestapi/BadgeColor.java | 12 - .../nevets/codebadgerestapi/BadgeFactory.java | 265 +++++++++--------- .../tech/nevets/codebadgerestapi/Server.java | 68 +++-- .../tech/nevets/codebadgerestapi/askjdf.java | 12 - 5 files changed, 180 insertions(+), 181 deletions(-) delete mode 100644 src/main/java/tech/nevets/codebadgerestapi/BadgeColor.java delete mode 100644 src/main/java/tech/nevets/codebadgerestapi/askjdf.java diff --git a/build.gradle b/build.gradle index 69a6cb8..bfa2326 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'tech.nevets' -version '0.1.0' +version '1.0.0' repositories { mavenCentral() @@ -15,10 +15,10 @@ repositories { } dependencies { - implementation 'org.jetbrains:annotations-java5:23.0.0' implementation 'com.sparkjava:spark-core:2.9.4' implementation 'org.slf4j:slf4j-simple:2.0.3' implementation 'com.google.code.gson:gson:2.10' + implementation 'org.jsoup:jsoup:1.15.3' implementation 'org.apache.xmlgraphics:batik-svggen:1.16' implementation 'org.apache.batik:org.apache.batik.dom:1.6.0-20081006' } diff --git a/src/main/java/tech/nevets/codebadgerestapi/BadgeColor.java b/src/main/java/tech/nevets/codebadgerestapi/BadgeColor.java deleted file mode 100644 index 471a0c6..0000000 --- a/src/main/java/tech/nevets/codebadgerestapi/BadgeColor.java +++ /dev/null @@ -1,12 +0,0 @@ -package tech.nevets.codebadgerestapi; - -public enum BadgeColor { - RED, - ORANGE, - YELLOW, - GREEN, - BLUE, - PURPLE, - LIGHTGRAY, - GRAY -} diff --git a/src/main/java/tech/nevets/codebadgerestapi/BadgeFactory.java b/src/main/java/tech/nevets/codebadgerestapi/BadgeFactory.java index a6d6912..11da460 100644 --- a/src/main/java/tech/nevets/codebadgerestapi/BadgeFactory.java +++ b/src/main/java/tech/nevets/codebadgerestapi/BadgeFactory.java @@ -5,22 +5,22 @@ import com.google.gson.JsonObject; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.svggen.SVGGraphics2DIOException; -import org.slf4j.LoggerFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; -import javax.imageio.ImageIO; import java.awt.*; -import java.awt.image.BufferedImage; import java.io.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.util.HashMap; +import java.util.Map; + public class BadgeFactory { - public static byte[] genSVGBadge(String leftStr, String rightStr, Color secondaryColor) { + public static byte[] genSVGBadge(String leftStr, String rightStr, Color secondaryColor, boolean isRounded) { DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); Document doc = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null); SVGGraphics2D graphics2D = new SVGGraphics2D(doc); @@ -30,12 +30,23 @@ public class BadgeFactory { FontMetrics fm = graphics2D.getFontMetrics(); int lWidth = fm.stringWidth(leftStr); int rWidth = fm.stringWidth(rightStr); - int totalWidth = lWidth = lWidth + rWidth + 40; + int totalWidth = lWidth + rWidth + 40; int totalHeight = 20; + + graphics2D.setSVGCanvasSize(new Dimension(totalWidth, totalHeight)); + graphics2D.setPaint(Color.GRAY); - graphics2D.fillRect(0, 0, totalWidth, totalHeight); + if (isRounded) { + graphics2D.fillRoundRect(0, 0, totalWidth, totalHeight, 10, 10); + } else { + graphics2D.fillRect(0, 0, totalWidth, totalHeight); + } graphics2D.setPaint(secondaryColor); - graphics2D.fillRect(totalWidth - rWidth - 20, 0, rWidth + 20, totalHeight); + if (isRounded) { + graphics2D.fillRoundRect(totalWidth - rWidth - 20, 0, rWidth + 20, totalHeight, 10, 10); + } else { + graphics2D.fillRect(totalWidth - rWidth - 20, 0, rWidth + 20, totalHeight); + } graphics2D.setPaint(Color.GRAY); graphics2D.drawString(leftStr, 10, (totalHeight / 2) + 6); graphics2D.drawString(rightStr, totalWidth - rWidth - 10, (totalHeight / 2) + 6); @@ -54,129 +65,88 @@ public class BadgeFactory { return baos.toByteArray(); } - public static byte[] generateBadge(String leftStr, String rightStr, BadgeColor secondaryColor) { - BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics2D = img.createGraphics(); - Font font = new Font("Calibri", Font.PLAIN, 23); - graphics2D.setFont(font); - FontMetrics fontMetrics = graphics2D.getFontMetrics(); - int lWidth = fontMetrics.stringWidth(leftStr); - int rWidth = fontMetrics.stringWidth(rightStr); - int height = fontMetrics.getHeight(); - graphics2D.dispose(); - - int totalWidth = lWidth + rWidth + 20; - int totalHeight = 25; - int textY = 19; - - BufferedImage baseBadgeImg; - BufferedImage secondaryImg; - try { - baseBadgeImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-base-gray.png")); - switch (secondaryColor) { - case RED -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-red.png")); - case ORANGE -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-orange.png")); - case YELLOW -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-yellow.png")); - case GREEN -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-green.png")); - case BLUE -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-blue.png")); - case PURPLE -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-purple.png")); - case LIGHTGRAY -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-color-light-gray.png")); - default -> secondaryImg = ImageIO.read(BadgeFactory.class.getResourceAsStream("/badge-base-gray.png")); + public static String getVersionFromGitBranch(String gitUrl, String org, String repo, String branch) { + String versionSourcesUrlBase; + if (org == null && repo == null) { + if (gitUrl.endsWith(".git")) { + versionSourcesUrlBase = gitUrl.substring(0, gitUrl.length() - 4) + "/raw/branch/" + branch; + } else { + versionSourcesUrlBase = gitUrl + "/raw/branch/" + branch; } - } catch (IOException e) { - e.printStackTrace(); - baseBadgeImg = null; - secondaryImg = null; + if (!gitUrl.startsWith("https://")) { + versionSourcesUrlBase = "https://" + versionSourcesUrlBase; + } + } else { + versionSourcesUrlBase = "https://" + gitUrl + "/" + org + "/" + repo + "/raw/branch/" + branch; } + Map VERSION_SOURCES = new HashMap<>(); + VERSION_SOURCES.put(Source.GRADLE_GROOVY, versionSourcesUrlBase + "/build.gradle"); + VERSION_SOURCES.put(Source.GRADLE_KOTLIN,versionSourcesUrlBase + "/build.gradle.kts"); + VERSION_SOURCES.put(Source.MAVEN, versionSourcesUrlBase + "/pom.xml"); + VERSION_SOURCES.put(Source.VERSION_FILE, versionSourcesUrlBase + "/VERSION"); - img = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_ARGB); - graphics2D = img.createGraphics(); - graphics2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); - graphics2D.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); - graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); - graphics2D.setFont(font); - graphics2D.drawImage(resizeImage(baseBadgeImg, totalWidth), 0, 0, totalWidth, totalHeight, null); - graphics2D.drawImage(secondaryImg, totalWidth - rWidth - 10, 0, rWidth + 10, totalHeight, null); - graphics2D.setColor(Color.GRAY); - graphics2D.drawString(leftStr, 5, textY - 1); - graphics2D.drawString(rightStr, totalWidth - rWidth - 5, textY - 1); - graphics2D.setColor(Color.WHITE); - graphics2D.drawString(leftStr, 5, textY); - graphics2D.drawString(rightStr, totalWidth - rWidth - 5, textY); - graphics2D.dispose(); + VersionSource vs = new VersionSource(); - ByteArrayOutputStream baos; - try { - baos = new ByteArrayOutputStream(); - ImageIO.write(img, "png", baos); - } catch (IOException e) { - e.printStackTrace(); - LoggerFactory.getLogger(BadgeFactory.class).info("not good thingy happened..."); - return new byte[]{}; - } - - return baos.toByteArray(); - } - - private static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth) { - BufferedImage resizedImage = new BufferedImage(targetWidth, 25, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics2D = resizedImage.createGraphics(); - graphics2D.drawImage(originalImage, 0, 0, targetWidth, 25, null); - graphics2D.dispose(); - return resizedImage; - } - - public static String getVersionFromGitRepo(String gitUrl, String org, String repo, String branch) { - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() - .GET() - .header("accept", "text/plain") - .uri(URI.create("https://" + gitUrl + "/" + org + "/" + repo + "/raw/branch/" + branch + "/build.gradle")) - .build(); - String responseString = ""; - try { - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - responseString = response.body(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - - String[] split = responseString.split("\n"); - String version = null; - for (String str : split) { - if (str.startsWith("version")) { - version = str.substring(9, str.length() - 1); + for (Map.Entry entry : VERSION_SOURCES.entrySet()) { + String content = getVersionIndicatorRaw(entry.getValue()); + if (content != null) { + vs.content = content; + vs.source = entry.getKey(); + break; } } - return version; + return extractVersionFromSource(vs); + } + + public static String getVersionFromGitCommit(String gitUrl, String org, String repo, String commit) { + String versionSourcesUrlBase; + if (org == null && repo == null) { + if (gitUrl.endsWith(".git")) { + versionSourcesUrlBase = gitUrl.substring(0, gitUrl.length() - 4) + "/raw/commit/" + commit; + } else { + versionSourcesUrlBase = gitUrl + "/raw/branch/" + commit; + } + if (!gitUrl.startsWith("https://")) { + versionSourcesUrlBase = "https://" + versionSourcesUrlBase; + } + } else { + versionSourcesUrlBase = "https://" + gitUrl + "/" + org + "/" + repo + "/raw/commit/" + commit; + } + Map VERSION_SOURCES = new HashMap<>(); + VERSION_SOURCES.put(Source.GRADLE_GROOVY, versionSourcesUrlBase + "/build.gradle"); + VERSION_SOURCES.put(Source.GRADLE_KOTLIN,versionSourcesUrlBase + "/build.gradle.kts"); + VERSION_SOURCES.put(Source.MAVEN, versionSourcesUrlBase + "/pom.xml"); + VERSION_SOURCES.put(Source.VERSION_FILE, versionSourcesUrlBase + "/VERSION"); + + VersionSource vs = new VersionSource(); + + for (Map.Entry entry : VERSION_SOURCES.entrySet()) { + String content = getVersionIndicatorRaw(entry.getValue()); + if (content != null) { + vs.content = content; + vs.source = entry.getKey(); + break; + } + } + + return extractVersionFromSource(vs); } public static String getLatestBuildStatusFromJenkins(String jenkinsUrl, String projectName) { - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() - .GET() - .header("accept", "application/json") - .uri(URI.create("https://" + jenkinsUrl + "/job/" + projectName + "/lastBuild/api/json")) - .build(); - String responseString = ""; - try { - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - responseString = response.body(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - - JsonObject json = new Gson().fromJson(responseString, JsonObject.class); - return json.get("result").getAsString(); + return getJenkinsProjectJson(jenkinsUrl, projectName).get("result").getAsString(); } public static String getLatestSuccessfulVersionFromJenkins(String jenkinsUrl, String projectName) { + JsonObject json = getJenkinsProjectJson(jenkinsUrl, projectName); + String gitUrl = json.get("actions").getAsJsonArray().get(2).getAsJsonObject().get("remoteUrls").getAsJsonArray().get(0).getAsString(); + gitUrl = gitUrl.substring(0, gitUrl.length() - 4); + String commit = json.get("changeSet").getAsJsonObject().get("items").getAsJsonArray().get(0).getAsJsonObject().get("commitId").getAsString(); + + return getVersionFromGitCommit(gitUrl, null, null, commit); + } + + private static JsonObject getJenkinsProjectJson(String jenkinsUrl, String projectName) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .GET() @@ -191,33 +161,64 @@ public class BadgeFactory { e.printStackTrace(); } - JsonObject json = new Gson().fromJson(responseString, JsonObject.class); - String gitUrl = json.get("actions").getAsJsonArray().get(2).getAsJsonObject().get("remoteUrls").getAsJsonArray().get(0).getAsString(); - gitUrl = gitUrl.substring(0, gitUrl.length() - 4); - String commit = json.get("changeSet").getAsJsonObject().get("items").getAsJsonArray().get(0).getAsJsonObject().get("commitId").getAsString(); + return new Gson().fromJson(responseString, JsonObject.class); + } + private static String getVersionIndicatorRaw(String url) { HttpClient gitClient = HttpClient.newHttpClient(); HttpRequest gitRequest = HttpRequest.newBuilder() .GET() .header("accept", "text/plain") - .uri(URI.create(gitUrl + "/raw/commit/" + commit + "/build.gradle")) + .uri(URI.create(url)) .build(); - String gitResponseString = ""; try { HttpResponse response = gitClient.send(gitRequest, HttpResponse.BodyHandlers.ofString()); - gitResponseString = response.body(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); + return response.body(); + } catch (final Exception e) { + return null; } + } - String[] split = gitResponseString.split("\n"); - String version = null; - for (String str : split) { - if (str.startsWith("version")) { - version = str.substring(9, str.length() - 1); + private static String extractVersionFromSource(VersionSource vs) { + String[] split = vs.content.split("\n"); + switch (vs.source) { + case GRADLE_GROOVY -> { + for (String str : split) { + if (str.startsWith("version")) { + return str.substring(9, str.length() - 1); + } + } + } + case GRADLE_KOTLIN -> { + for (String str : split) { + if (str.startsWith("version =")) { + return str.substring(10, str.length() - 1); + } + } + } + case MAVEN -> { + for (String str : split) { + if (str.startsWith(" ")) { + return str.substring(11, str.length() - 1); + } + } + } + case VERSION_FILE -> { + return vs.content; } } + return "unknown"; + } - return version; + enum Source { + GRADLE_GROOVY, + GRADLE_KOTLIN, + MAVEN, + VERSION_FILE + } + + private static class VersionSource { + private Source source; + private String content; } } diff --git a/src/main/java/tech/nevets/codebadgerestapi/Server.java b/src/main/java/tech/nevets/codebadgerestapi/Server.java index d11c3ba..aa3937e 100644 --- a/src/main/java/tech/nevets/codebadgerestapi/Server.java +++ b/src/main/java/tech/nevets/codebadgerestapi/Server.java @@ -1,17 +1,24 @@ package tech.nevets.codebadgerestapi; +import org.jsoup.Jsoup; +import org.jsoup.safety.Safelist; + import java.awt.*; import static spark.Spark.*; public class Server { public static void main(String[] args) { + port(8080); path("/badge", () -> { before("/*"); - get("/test/:left/:right/:color", (req, res) -> { + + get("/test/:left/:right", (req, res) -> { + String left = Jsoup.clean(req.params(":left"), Safelist.relaxed()); + String right = Jsoup.clean(req.params(":right"), Safelist.relaxed()); res.type("image/svg+xml"); - Color color = Color.GRAY; - switch (req.params(":color").toLowerCase()) { + Color color; + switch (req.queryParams("color").toLowerCase()) { case "red" -> color = Color.RED; case "orange" -> color = Color.ORANGE; case "yellow" -> color = Color.YELLOW; @@ -26,12 +33,30 @@ public class Server { case "black" -> color = Color.BLACK; default -> color = Color.GRAY; } - return BadgeFactory.genSVGBadge(req.params(":left"), req.params(":right"), color); + boolean isRounded; + if (req.queryParams("rounded") == null) { + isRounded = false; + } else { + if (req.queryParams("rounded").equals("true")) { + isRounded = true; + } else { + isRounded = false; + } + } + return BadgeFactory.genSVGBadge(left, right, color, isRounded); }); + get("/version/*/:gitUrl/:org/:repo/:branch", (req, res) -> { - switch (req.splat()[0]) { + String splat0 = Jsoup.clean(req.splat()[0], Safelist.relaxed()); + String gitUrl = Jsoup.clean(req.params(":gitUrl"), Safelist.relaxed()); + String org = Jsoup.clean(req.params(":org"), Safelist.relaxed()); + String repo = Jsoup.clean(req.params(":repo"), Safelist.relaxed()); + String branch = Jsoup.clean(req.params(":branch"), Safelist.relaxed()); + switch (splat0) { case "gitea" -> { - return BadgeFactory.getVersionFromGitRepo(req.params(":gitUrl"), req.params(":org"), req.params(":repo"), req.params(":branch")); + res.type("image/svg+xml"); + String version = BadgeFactory.getVersionFromGitBranch(gitUrl, org, repo, branch); + return BadgeFactory.genSVGBadge("Version", version, Color.GREEN, true); } case "github" -> { res.status(405); @@ -41,33 +66,31 @@ public class Server { res.status(405); return "Not yet Implemented!"; } - case "test" -> { - res.type("image/png"); - String version = BadgeFactory.getVersionFromGitRepo(req.params(":gitUrl"), req.params(":org"), req.params(":repo"), req.params(":branch")); - return BadgeFactory.generateBadge("Version", version, BadgeColor.GREEN); - } } res.status(404); return "Not Found!"; }); get("/buildStatus/*/:ciUrl/:projectName", (req, res) -> { - switch (req.splat()[0]) { + String splat0 = Jsoup.clean(req.splat()[0], Safelist.relaxed()); + String ciUrl = Jsoup.clean(req.params(":ciUrl"), Safelist.relaxed()); + String projectName = Jsoup.clean(req.params(":projectName"), Safelist.relaxed()); + switch (splat0) { case "jenkins" -> { res.type("image/png"); - String buildStatus = BadgeFactory.getLatestBuildStatusFromJenkins(req.params(":ciUrl"), req.params(":projectName")); - BadgeColor badgeColor = BadgeColor.GRAY; + String buildStatus = BadgeFactory.getLatestBuildStatusFromJenkins(ciUrl, projectName); + Color badgeColor = Color.GRAY; switch (buildStatus) { case "SUCCESS" -> { buildStatus = "passing"; - badgeColor = BadgeColor.GREEN; + badgeColor = Color.GREEN; } case "FAILURE" -> { buildStatus = "failing"; - badgeColor = BadgeColor.RED; + badgeColor = Color.RED; } } - return BadgeFactory.generateBadge("Build", buildStatus, badgeColor); + return BadgeFactory.genSVGBadge("Build", buildStatus, badgeColor, true); } case "circleci" -> { res.status(405); @@ -77,19 +100,18 @@ public class Server { res.status(405); return "Not yet Implemented!"; } - case "test" -> { - res.type("image/svg+xml"); - - } } res.status(404); return "Not Found!"; }); get("/latestSuccessfulVersion/*/:ciUrl/:projectName", (req, res) -> { - switch (req.splat()[0]) { + String splat0 = Jsoup.clean(req.splat()[0], Safelist.relaxed()); + String ciUrl = Jsoup.clean(req.params(":ciUrl"), Safelist.relaxed()); + String projectName = Jsoup.clean(req.params(":projectName"), Safelist.relaxed()); + switch (splat0) { case "jenkins" -> { - return BadgeFactory.getLatestSuccessfulVersionFromJenkins(req.params(":ciUrl"), req.params(":projectName")); + return BadgeFactory.getLatestSuccessfulVersionFromJenkins(ciUrl, projectName); } case "circleci" -> { res.status(405); diff --git a/src/main/java/tech/nevets/codebadgerestapi/askjdf.java b/src/main/java/tech/nevets/codebadgerestapi/askjdf.java deleted file mode 100644 index c8c4d43..0000000 --- a/src/main/java/tech/nevets/codebadgerestapi/askjdf.java +++ /dev/null @@ -1,12 +0,0 @@ -package tech.nevets.codebadgerestapi; - -import java.awt.*; - -public class askjdf { - public static void main(String[] args) { - - for (Font font : GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts()) { - System.out.println(font.getName()); - } - } -}