From 461dc6cbfdc82e6cae3f7ae2ef073543852e8cc5 Mon Sep 17 00:00:00 2001 From: thc202 Date: Mon, 8 Jan 2024 11:42:54 +0000 Subject: [PATCH] Update getting started website page and PDF Update the page and copy the PDF file when the gettingStarted add-on is released. Signed-off-by: thc202 --- build.gradle.kts | 19 ++++ .../UpdateGettingStartedWebsitePage.java | 107 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 buildSrc/src/main/java/org/zaproxy/gradle/UpdateGettingStartedWebsitePage.java diff --git a/build.gradle.kts b/build.gradle.kts index 9d41517e..aebde949 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,9 +16,11 @@ import org.zaproxy.gradle.HandleWeeklyRelease import org.zaproxy.gradle.UpdateAddOnZapVersionsEntries import org.zaproxy.gradle.UpdateAndCreatePullRequestAddOnRelease import org.zaproxy.gradle.UpdateDailyZapVersionsEntries +import org.zaproxy.gradle.UpdateGettingStartedWebsitePage import org.zaproxy.gradle.UpdateMainZapVersionsEntries import org.zaproxy.gradle.UpdateZapVersionWebsiteData import org.zaproxy.gradle.crowdin.DeployCrowdinTranslations +import java.util.Optional plugins { java @@ -282,6 +284,18 @@ val generateWebsitePages by tasks.registering(GenerateWebsitePages::class) { outputDir.set(layout.buildDirectory.dir("websiteHelpPages")) } +val updateGettingStartedWebsitePage by tasks.registering(UpdateGettingStartedWebsitePage::class) { + addOn.set( + downloadReleasedAddOns.map { + var files = fileTree(it.outputDir).matching { include("gettingStarted*.zap") }.files + if (files.isEmpty()) Optional.empty() else Optional.of(files.first()) + }, + ) + filenameRegex.set("ZAPGettingStartedGuide-.+\\.pdf") + gettingStartedPage.set(file("$siteDir/content/getting-started/index.md")) + pdfDirectory.set(file("$siteDir/static/pdf/")) +} + val generateWebsiteSbomPages by tasks.registering(GenerateWebsiteSbomPages::class) { releaseState.set(releaseStateData) zapVersions.set(latestZapVersions) @@ -315,8 +329,13 @@ updateZapVersionWebsiteData { mustRunAfter(copyWebsiteGeneratedData) } +updateGettingStartedWebsitePage { + mustRunAfter(copyWebsiteGeneratedData) +} + val updateWebsite by tasks.registering(CreatePullRequest::class) { dependsOn(copyWebsiteGeneratedData) + dependsOn(updateGettingStartedWebsitePage) dependsOn(updateZapVersionWebsiteData) user.set(ghUser) diff --git a/buildSrc/src/main/java/org/zaproxy/gradle/UpdateGettingStartedWebsitePage.java b/buildSrc/src/main/java/org/zaproxy/gradle/UpdateGettingStartedWebsitePage.java new file mode 100644 index 00000000..0d3500b2 --- /dev/null +++ b/buildSrc/src/main/java/org/zaproxy/gradle/UpdateGettingStartedWebsitePage.java @@ -0,0 +1,107 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zaproxy.gradle; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Iterator; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.gradle.api.DefaultTask; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; + +public abstract class UpdateGettingStartedWebsitePage extends DefaultTask { + + @Input + public abstract Property> getAddOn(); + + @Input + public abstract Property getFilenameRegex(); + + @Input + public abstract Property getGettingStartedPage(); + + @Input + public abstract Property getPdfDirectory(); + + @TaskAction + void executeTasks() throws Exception { + Optional optionalAddOn = getAddOn().get(); + if (optionalAddOn.isEmpty()) { + return; + } + + File addOn = optionalAddOn.get(); + Pattern filenamePattern = Pattern.compile(getFilenameRegex().get()); + String filename = null; + + try (ZipFile zip = new ZipFile(addOn)) { + boolean found = false; + for (Iterator it = zip.entries().asIterator(); + it.hasNext() && !found; ) { + ZipEntry entry = it.next(); + if (!filenamePattern.matcher(entry.getName()).find()) { + continue; + } + + found = true; + filename = extractFilename(entry.getName()); + try (InputStream zis = zip.getInputStream(entry)) { + Path target = getPdfDirectory().get().toPath().resolve(filename); + Files.copy(zis, target, StandardCopyOption.REPLACE_EXISTING); + } + } + } + + if (filename == null) { + throw new IOException( + "No file matching the provided filename pattern was found in the add-on."); + } + + updateFilename(filename, filenamePattern, getGettingStartedPage().get().toPath()); + } + + private static String extractFilename(String path) { + return path.substring(path.indexOf('/') + 1); + } + + private static void updateFilename(String filename, Pattern filenamePattern, Path file) + throws IOException { + String contents = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); + Matcher matcher = filenamePattern.matcher(contents); + if (!matcher.find()) { + throw new IOException("The filename pattern was not found in: " + file); + } + StringBuilder sb = new StringBuilder(); + matcher.appendReplacement(sb, filename); + matcher.appendTail(sb); + Files.write(file, sb.toString().getBytes(StandardCharsets.UTF_8)); + } +}