diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e945d01abfb..5473937fa5d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,3 +25,4 @@ updates: - dependency-name: "com.google.errorprone:error_prone_core" versions: [ ">= 2.43.0" ] # versions 2.43.x and later require Java 21 - dependency-name: "com.diffplug.spotless*" # newer versions lead to runtime error, will need to update the common zap plugin first + - dependency-name: "org.jgrapht:jgrapht-core" # Also used by AJAX Spider and newer versions have breaking changes. diff --git a/LEGALNOTICE.md b/LEGALNOTICE.md index ee05ff2a85c..23efba5d370 100644 --- a/LEGALNOTICE.md +++ b/LEGALNOTICE.md @@ -32,32 +32,32 @@ and subject to their respective licenses. | Library | License | |-------------------------------------|---------------------------| | commons-beanutils-1.11.0.jar | Apache 2.0 | -| commons-codec-1.20.0.jar | Apache 2.0 | +| commons-codec-1.21.0.jar | Apache 2.0 | | commons-collections-3.2.2.jar | Apache 2.0 | | commons-configuration-1.10.jar | Apache 2.0 | | commons-csv-1.14.1.jar | Apache 2.0 | | commons-httpclient-3.1.jar | Apache 2.0 | | commons-io-2.21.0.jar | Apache 2.0 | | commons-lang-2.6.jar | Apache 2.0 | -| commons-lang3-3.19.0.jar | Apache 2.0 | -| commons-logging-1.3.5.jar | Apache 2.0 | -| commons-text-1.14.0.jar | Apache 2.0 | +| commons-lang3-3.20.0.jar | Apache 2.0 | +| commons-logging-1.3.6.jar | Apache 2.0 | +| commons-text-1.15.0.jar | Apache 2.0 | | ezmorph-1.0.6.jar | Apache 2.0 | -| flatlaf-3.7.jar | Apache 2.0 | -| flatlaf-swingx-3.7.jar | Apache 2.0 | +| flatlaf-3.7.1.jar | Apache 2.0 | +| flatlaf-swingx-3.7.1.jar | Apache 2.0 | | harlib-1.1.3.jar | Apache 2.0 | | hsqldb-2.7.4.jar | BSD | | jackson-core-asl-1.9.13.jar | Apache 2.0 | -| javahelp-2.0.05.jar | GPL + classpath exception | | java-semver-0.10.2.jar | MIT | +| javahelp-2.0.05.jar | GPL + classpath exception | | jericho-html-3.4.jar | EPL / LGPL dual license | | jfreechart-1.5.6.jar | LGPL | | jgrapht-core-0.9.2.jar | LGPL 2.1 | | json-lib-2.4-jdk15.jar | MIT + "Good, Not Evil" | -| log4j-1.2-api-2.25.3.jar | Apache 2.0 | -| log4j-api-2.25.3.jar | Apache 2.0 | -| log4j-core-2.25.3.jar | Apache 2.0 | -| log4j-jul-2.25.3.jar | Apache 2.0 | -| rsyntaxtextarea-3.6.0.jar | BSD-3 clause | +| log4j-1.2-api-2.25.4.jar | Apache 2.0 | +| log4j-api-2.25.4.jar | Apache 2.0 | +| log4j-core-2.25.4.jar | Apache 2.0 | +| log4j-jul-2.25.4.jar | Apache 2.0 | +| rsyntaxtextarea-3.6.2.jar | BSD-3 clause | | swingx-all-1.6.5-1.jar | LGPL 2.1 | -| xom-1.3.9.jar | LGPL | +| xom-1.4.0.jar | LGPL | diff --git a/buildSrc/src/main/java/org/zaproxy/zap/tasks/UpdateLegalNotice.java b/buildSrc/src/main/java/org/zaproxy/zap/tasks/UpdateLegalNotice.java new file mode 100644 index 00000000000..491701098de --- /dev/null +++ b/buildSrc/src/main/java/org/zaproxy/zap/tasks/UpdateLegalNotice.java @@ -0,0 +1,163 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2026 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.zap.tasks; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.component.ModuleComponentIdentifier; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +/** + * A task that updates the third-party library table in LEGALNOTICE.md. + * + *

Reads library licenses from a YAML mapping file and replaces the table (from the table header + * to end of file) in the legal notice file with a freshly generated one. + */ +public abstract class UpdateLegalNotice extends DefaultTask { + + private static final String LIBRARY_HEADER = "Library"; + private static final String LICENSE_HEADER = "License"; + + /** The runtime classpath configuration to inspect for third-party libraries. */ + @InputFiles + @PathSensitive(PathSensitivity.NAME_ONLY) + public abstract Property getConfiguration(); + + /** + * The YAML file mapping Maven {@code groupId:artifactId} coordinates to their license strings. + */ + @InputFile + @PathSensitive(PathSensitivity.NONE) + public abstract RegularFileProperty getLicensesFile(); + + /** The legal notice file to update. */ + @OutputFile + public abstract RegularFileProperty getLegalNoticeFile(); + + @TaskAction + public void update() throws IOException { + Set rows = + createRows( + getConfiguration().get().getResolvedConfiguration().getResolvedArtifacts(), + getLicensesFile().get().getAsFile().toPath()); + + int libWidth = Math.max(LIBRARY_HEADER.length(), 35); + int licWidth = LICENSE_HEADER.length(); + for (TableRow row : rows) { + libWidth = Math.max(libWidth, row.fileName().length()); + licWidth = Math.max(licWidth, row.licenseName().length()); + } + + Path legalNoticeFile = getLegalNoticeFile().get().getAsFile().toPath(); + + StringBuilder fileContent = new StringBuilder(); + fileContent.append(readMainContent(legalNoticeFile)); + + String rowPattern = "| %-" + libWidth + "s | %-" + licWidth + "s |\n"; + appendRow(fileContent, rowPattern, LIBRARY_HEADER, LICENSE_HEADER); + appendSeparator(fileContent, libWidth, licWidth); + for (TableRow row : rows) { + appendRow(fileContent, rowPattern, row.fileName(), row.licenseName()); + } + + Files.writeString(legalNoticeFile, fileContent); + } + + @SuppressWarnings("unchecked") + private static Set createRows(Set artifacts, Path licensesFile) + throws IOException { + Map yaml = + new ObjectMapper(new YAMLFactory()).readValue(licensesFile.toFile(), Map.class); + Map licenseMap = (Map) yaml.get("licenses"); + if (licenseMap == null) { + throw new GradleException( + "Licenses YAML file is missing the 'licenses' key: " + licensesFile); + } + + Set rows = new TreeSet<>(Comparator.comparing(TableRow::fileName)); + for (ResolvedArtifact artifact : artifacts) { + ModuleComponentIdentifier id = + (ModuleComponentIdentifier) artifact.getId().getComponentIdentifier(); + String coordinates = id.getGroup() + ":" + id.getModule(); + String fileName = artifact.getFile().getName(); + String license = licenseMap.get(coordinates); + if (license == null) { + throw new GradleException( + "No license mapping found for '" + + coordinates + + "' (JAR: " + + fileName + + "). Add it to: " + + licensesFile.getFileName()); + } + rows.add(new TableRow(fileName, license)); + } + + return rows; + } + + private static String readMainContent(Path legalNoticeFile) throws IOException { + String content = Files.readString(legalNoticeFile); + int tableStart = content.indexOf("\n| " + LIBRARY_HEADER); + if (tableStart == -1) { + throw new GradleException( + "Could not find table header ('| " + + LIBRARY_HEADER + + "') in: " + + legalNoticeFile); + } + return content.substring(0, tableStart + 1); + } + + private static void appendRow( + StringBuilder fileContent, String rowPattern, String library, String license) { + fileContent.append(String.format(rowPattern, library, license)); + } + + private static void appendSeparator(StringBuilder fileContent, int libWidth, int licWidth) { + fileContent + .append('|') + .append("-".repeat(libWidth + 2)) + .append("|") + .append("-".repeat(licWidth + 2)) + .append("|") + .append('\n'); + } + + private record TableRow(String fileName, String licenseName) {} +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 174b14386aa..38dbeb44a27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,22 +1,22 @@ [versions] -errorprone = "2.36.0" -flatlaf = "3.7" -log4j = "2.25.3" +errorprone = "2.42.0" +flatlaf = "3.7.1" +log4j = "2.25.4" [libraries] -bndAnnotation = "biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0" -bytebuddy = "net.bytebuddy:byte-buddy:1.18.0" +bndAnnotation = "biz.aQute.bnd:biz.aQute.bnd.annotation:7.2.3" +bytebuddy = "net.bytebuddy:byte-buddy:1.18.8" commons-beanutils = "commons-beanutils:commons-beanutils:1.11.0" -commons-codec = "commons-codec:commons-codec:1.20.0" +commons-codec = "commons-codec:commons-codec:1.21.0" commons-collections = "commons-collections:commons-collections:3.2.2" commons-configuration = "commons-configuration:commons-configuration:1.10" commons-csv = "org.apache.commons:commons-csv:1.14.1" commons-httpclient = "commons-httpclient:commons-httpclient:3.1" commons-io = "commons-io:commons-io:2.21.0" commons-lang = "commons-lang:commons-lang:2.6" -commons-lang3 = "org.apache.commons:commons-lang3:3.19.0" -commons-logging = "commons-logging:commons-logging:1.3.5" -commons-text = "org.apache.commons:commons-text:1.14.0" +commons-lang3 = "org.apache.commons:commons-lang3:3.20.0" +commons-logging = "commons-logging:commons-logging:1.3.6" +commons-text = "org.apache.commons:commons-text:1.15.0" errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone" } findbugsAnnotations = "com.google.code.findbugs:findbugs-annotations:3.0.1" flatlaf = { module = "com.formdev:flatlaf", version.ref = "flatlaf" } @@ -34,23 +34,23 @@ log4j-api12 = { module = "org.apache.logging.log4j:log4j-1.2-api", version.ref = log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } log4j-slf4j = { module = "org.apache.logging.log4j:log4j-slf4j-impl", version.ref = "log4j" } -rsyntaxtextarea = "com.fifesoft:rsyntaxtextarea:3.6.0" +rsyntaxtextarea = "com.fifesoft:rsyntaxtextarea:3.6.2" swingx = "org.swinglabs.swingx:swingx-all:1.6.5-1" -xom = "xom:xom:1.3.9" +xom = "xom:xom:1.4.0" -assertj-core = "org.assertj:assertj-core:3.27.6" +assertj-core = "org.assertj:assertj-core:3.27.7" assertj-swing = "org.assertj:assertj-swing:3.17.1" hamcrest-core = "org.hamcrest:hamcrest-core:3.0" -junit-jupiter = "org.junit.jupiter:junit-jupiter:6.0.1" +junit-jupiter = "org.junit.jupiter:junit-jupiter:6.0.3" junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } -mockito = "org.mockito:mockito-junit-jupiter:5.20.0" +mockito = "org.mockito:mockito-junit-jupiter:5.23.0" [plugins] crowdin = "org.zaproxy.crowdin:0.6.0" cyclonedx.id = "org.cyclonedx.bom" -dependencyUpdates = "com.github.ben-manes.versions:0.52.0" -errorprone = "net.ltgt.errorprone:4.1.0" +dependencyUpdates = "com.github.ben-manes.versions:0.54.0" +errorprone = "net.ltgt.errorprone:5.1.0" japicmp.id = "me.champeau.gradle.japicmp" -sonarqube = "org.sonarqube:6.0.1.5171" +sonarqube = "org.sonarqube:7.2.3.7755" spotless.id = "com.diffplug.spotless" zaproxy-common.id = "org.zaproxy.common" diff --git a/zap/gradle/thirdPartyLicenses.yaml b/zap/gradle/thirdPartyLicenses.yaml new file mode 100644 index 00000000000..37d32ad811d --- /dev/null +++ b/zap/gradle/thirdPartyLicenses.yaml @@ -0,0 +1,33 @@ +# Maps Maven groupId:artifactId to the license of the distributed library. +# Used by the updateLegalNotice Gradle task to regenerate the table in LEGALNOTICE.md. +licenses: + com.fifesoft:rsyntaxtextarea: "BSD-3 clause" + com.formdev:flatlaf: "Apache 2.0" + com.formdev:flatlaf-swingx: "Apache 2.0" + com.github.zafarkhaja:java-semver: "MIT" + commons-beanutils:commons-beanutils: "Apache 2.0" + commons-codec:commons-codec: "Apache 2.0" + commons-collections:commons-collections: "Apache 2.0" + commons-configuration:commons-configuration: "Apache 2.0" + commons-httpclient:commons-httpclient: "Apache 2.0" + commons-io:commons-io: "Apache 2.0" + commons-lang:commons-lang: "Apache 2.0" + commons-logging:commons-logging: "Apache 2.0" + edu.umass.cs.benchlab:harlib: "Apache 2.0" + javax.help:javahelp: "GPL + classpath exception" + net.htmlparser.jericho:jericho-html: "EPL / LGPL dual license" + net.sf.ezmorph:ezmorph: "Apache 2.0" + net.sf.json-lib:json-lib: 'MIT + "Good, Not Evil"' + org.apache.commons:commons-csv: "Apache 2.0" + org.apache.commons:commons-lang3: "Apache 2.0" + org.apache.commons:commons-text: "Apache 2.0" + org.apache.logging.log4j:log4j-1.2-api: "Apache 2.0" + org.apache.logging.log4j:log4j-api: "Apache 2.0" + org.apache.logging.log4j:log4j-core: "Apache 2.0" + org.apache.logging.log4j:log4j-jul: "Apache 2.0" + org.codehaus.jackson:jackson-core-asl: "Apache 2.0" + org.hsqldb:hsqldb: "BSD" + org.jfree:jfreechart: "LGPL" + org.jgrapht:jgrapht-core: "LGPL 2.1" + org.swinglabs.swingx:swingx-all: "LGPL 2.1" + xom:xom: "LGPL" diff --git a/zap/zap.gradle.kts b/zap/zap.gradle.kts index 802ad716174..cdce4fbe844 100644 --- a/zap/zap.gradle.kts +++ b/zap/zap.gradle.kts @@ -3,6 +3,7 @@ import me.champeau.gradle.japicmp.JapicmpTask import org.zaproxy.gradle.spotless.ValidateImports import org.zaproxy.zap.japicmp.AcceptMethodAbstractNowDefaultRule import org.zaproxy.zap.tasks.GradleBuildWithGitRepos +import org.zaproxy.zap.tasks.UpdateLegalNotice import org.zaproxy.zap.tasks.internal.JapicmpExcludedData import java.time.LocalDate import java.util.stream.Collectors @@ -332,3 +333,12 @@ fun zapJar(version: String): File { group = oldGroup } } + +tasks.register("updateLegalNotice") { + group = "ZAP Misc" + description = "Updates the third-party library table in LEGALNOTICE.md." + + configuration.set(configurations.runtimeClasspath) + licensesFile.set(file("gradle/thirdPartyLicenses.yaml")) + legalNoticeFile.set(rootProject.file("LEGALNOTICE.md")) +}