[java-mvn-enforcer] 01/05: JPAR-190 Investigate an enforcer to check all jars and poms ~/.m2/.... towards the end of a build
Rod Widdowson
rdw at steadingsoftware.com
Sun Oct 10 13:34:35 UTC 2021
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch main
in repository java-mvn-enforcer.
View the commit online:
http://git.shibboleth.net/view/?p=java-mvn-enforcer.git;a=commit;h=eb5fc6f8e6f525e803e98ad2a72aa8e0d1989e3f
commit eb5fc6f8e6f525e803e98ad2a72aa8e0d1989e3f
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Wed Oct 6 15:04:06 2021 +0100
JPAR-190 Investigate an enforcer to check all jars and poms ~/.m2/.... towards the end of a build
https://shibboleth.atlassian.net/browse/JPAR-190
Add a new class which will traverse a maven repo looking for signed jars.
Refact the existing sig checker to allow sharing much of the code.
---
.../impl/{SigChecker.java => BaseSigChecker.java} | 130 ++++++++---------
.../shibboleth/mvn/enforcer/impl/M2SigChecker.java | 99 +++++++++++++
.../mvn/enforcer/impl/ProjectPomContext.java | 7 +
.../shibboleth/mvn/enforcer/impl/SigChecker.java | 156 ++-------------------
4 files changed, 174 insertions(+), 218 deletions(-)
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
similarity index 67%
copy from src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java
copy to src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
index f7139b3..9edf19f 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/BaseSigChecker.java
@@ -5,7 +5,7 @@
* copyright ownership. The UCAID licenses this file to You 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
- *
+ *0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
@@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Security;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -37,17 +36,15 @@ import org.slf4j.Logger;
import net.shibboleth.mvn.enforcer.impl.GPGKeyRing.Signature;
import net.shibboleth.mvn.enforcer.impl.ParsedPom.PomArtifact;
-import net.shibboleth.utilities.java.support.collection.Pair;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * A class to iterate over the provided directories for jar files and test
- * their signatures.
+ * A class to iterate which understands signatures on jar files and maven.
*/
-public class SigChecker {
+public class BaseSigChecker {
/** Our log. */
- private final Logger log = EnforcerLogger.getLogger(SigChecker.class);
+ private final Logger log = EnforcerLogger.getLogger(BaseSigChecker.class);
/** where we are writing to (target/dependencyReport.txt).*/
private final PrintWriter report;
@@ -60,13 +57,13 @@ public class SigChecker {
/** The injected thing that collects maven artifacts. */
private final MavenLoader mavenLoader;
-
+
/** Constructor.
* @param loader How to get artifacts
* @param project The project
* @param writer Where to write our report
*/
- public SigChecker(@Nonnull final MavenLoader loader,
+ public BaseSigChecker(@Nonnull final MavenLoader loader,
@Nonnull final ProjectPomContext project,
@Nonnull final PrintWriter writer) {
mavenLoader = Constraint.isNotNull(loader, "Loader must not be null");
@@ -77,94 +74,83 @@ public class SigChecker {
}
}
- /** The Body of the signature test. Are all the files what we expected?
- * @param roots where to start looking
- * @return true if all was OK.
+ /** Get the {@link ProjectPomContext} for the operation.
+ * @return {@link #projectContext}
*/
- public boolean testSignatures(final List<Path> roots) {
- int sigFails = 0;
- for (final Path root:roots) {
- try {
- sigFails += Files.list(root).mapToInt(e -> checkSignature(e)).sum();
- } catch (final IOException e) {
- log.error("Failed to enumerate files at {}", root, e);
- return false;
- }
- }
- if (sigFails != 0) {
- report.format("\t%d non-exempt jar files did not have valid signatures\n", sigFails);
- } else {
- report.format("\tAll non-exempt jar files correctly signed\n");
- }
- return sigFails == 0;
+ protected ProjectPomContext getProjectContext() {
+ return projectContext;
}
+ /** Get the {@link MavenLoader} for the operation.
+ * @return {@link #mavenLoader}
+ */
+ protected MavenLoader getMavenLoader() {
+ return mavenLoader;
+ }
+
+ /** Get the {@link PrintWriter} used for reporting.
+ * @return {@link #report}
+ */
+ protected PrintWriter getReport() {
+ return report;
+ }
+
+
/** Given the Path and the parent dir check the signature.
* @param jarFile the file to check
- * @return 1 if anything went wrong
+ * @param artifact the {@link PomArtifact} for the jar file.
+ * @return true if the signature passed (or some other "usual conditions)
*/
- // CheckStyle: ReturnCount|CyclomaticComplexity OFF
- private int checkSignature(final Path jarFile) {
- final String fileName = jarFile.getFileName().toString();
- if (!fileName.endsWith(".jar")) {
- return 0;
- }
- final Pair<String,String> name = ProjectPomContext.splitFileName(fileName);
- final String group = projectContext.getGroup(name.getFirst());
- final PomArtifact jarAsArtifact =
- projectContext.getParentPom().new PomArtifact(group, name.getFirst(), name.getSecond());
- if (projectContext.getParentPom().getGeneratedArtifacts().contains(jarAsArtifact)) {
- report.format("%-30s: %-14s Generated by build. Not checked\n", name.getFirst(), name.getSecond());
- return 0;
- }
- if (group == null) {
- report.format("%-30s: %-14s Could not determine group\n", name.getFirst(), name.getSecond());
- log.info("{} {} could not determine group", name.getFirst(), name.getSecond());
- return 1;
- }
- if (projectContext.isSnapShot() && name.getSecond().endsWith("-SNAPSHOT")) {
- report.format("%-30s: %-14s Snapshot version on a snapshot build. Not Checked\n",
- name.getFirst(), name.getSecond());
- return 0;
- }
+ protected boolean checkSignature(final Path jarFile, final PomArtifact artifact) {
+ final String group = artifact.getGroupId();
+ final String id = artifact.getArtifactId();
+ final String version = artifact.getVersion();
+
+ if (projectContext.getParentPom().getGeneratedArtifacts().contains(artifact)) {
+ report.format("%-30s: %-14s Generated by build. Not checked\n", id, version);
+ return true;
+ }
+ if (projectContext.isSnapShot() && version.endsWith("-SNAPSHOT")) {
+ report.format("%-30s: %-14s Snapshot version on a snapshot build. Not Checked\n", id, version);
+ return true;
+ }
+
final GPGKeyRing keyRing = getKeyRing(group);
if (keyRing == null) {
- report.format("%-30s: %-14s No keyring for group %s\n", name.getFirst(), name.getSecond(), group);
- log.info("{} {} no keyring for group", name.getFirst(), name.getSecond(), group);
- return 1;
+ report.format("%-30s: %-14s No keyring for group %s\n", id, version, group);
+ log.info("{} {} no keyring for group", id, version, group);
+ return false;
}
- final Signature sig = getSignature(jarAsArtifact);
+ final Signature sig = getSignature(artifact);
if (sig == null) {
- report.format("%-30s: %-14s Could not find signature (group : %s)\n",
- name.getFirst(), name.getSecond(), group);
- log.info("{} {} could not find signature (group={})", name.getFirst(), name.getSecond(), group);
- return 1;
+ report.format("%-30s: %-14s Could not find signature (group : %s)\n", id, version, group);
+ log.info("{} {} could not find signature (group={})", id, version, group);
+ return false;
}
if (!keyRing.contains(sig)) {
report.format("%-30s: %-14s KeyId (%s) not found in keyring for %s\n",
- name.getFirst(), name.getSecond(), sig.toString(), group);
+ id, version, sig.toString(), group);
log.info("{} {} KeyId ({}) not found in keyring for {}",
- name.getFirst(), name.getSecond(), sig.toString(), group);
- return 1;
+ id, version, sig.toString(), group);
+ return false;
}
try (final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(jarFile.toFile()))) {
if (!keyRing.checkSignature(stream, sig)) {
report.format("%-30s: %-14s Signature Mismatch : %s in keyring %s\n",
- name.getFirst(), name.getSecond(), keyRing.getKeyInfo(sig), group);
+ id, version, keyRing.getKeyInfo(sig), group);
log.info("{} {} Signature Mismatch : ({}) in keyring for {}",
- name.getFirst(), name.getSecond(), sig.toString(), group);
- return 1;
+ id, version, sig.toString(), group);
+ return false;
}
} catch (final IOException e) {
log.error("Failed", e);
- return 1;
+ return false;
}
report.format("%-30s: %-14s Signature Match in keyring %s : %s \n",
- name.getFirst(), name.getSecond(), group, keyRing.getKeyInfo(sig));
- return 0;
+ id, version, group, keyRing.getKeyInfo(sig));
+ return true;
}
- // CheckStyle: ReturnCount|CyclomaticComplexity ON
/** Locate and load the signature for this artifact.
* @param artifact what to load
@@ -226,5 +212,5 @@ public class SigChecker {
keyRings.put(group, Optional.empty());
return null;
}
- }
+ }
}
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/M2SigChecker.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/M2SigChecker.java
new file mode 100644
index 0000000..c2015dc
--- /dev/null
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/M2SigChecker.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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
+ *0
+ * 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 net.shibboleth.mvn.enforcer.impl;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+
+/**
+ * A class to traverse over the provided ~/.m2 directory looking for jar files
+ * and testing their signatures.
+ */
+public class M2SigChecker extends BaseSigChecker {
+
+ /** Our log. */
+ private final Logger log = EnforcerLogger.getLogger(M2SigChecker.class);
+
+ private int failCount;
+
+ /** Constructor.
+ * @param loader How to get artifacts
+ * @param project The project
+ * @param writer Where to write our report
+ */
+ public M2SigChecker(@Nonnull final MavenLoader loader,
+ @Nonnull final ProjectPomContext project,
+ @Nonnull final PrintWriter writer) {
+ super(loader, project, writer);
+ }
+
+ /** The Body of the signature test. Are all the files what we expected?
+ * @param root where to start looking
+ * @return true if all was OK.
+ */
+ public boolean testSignatures(final Path root) {
+ try {
+ Files.walkFileTree(root, new M2Visitor());
+ } catch (final IOException e) {
+ log.error("Failed traversal", e);
+ return false;
+ }
+ return failCount == 0;
+ }
+
+ private class M2Visitor extends SimpleFileVisitor<Path> {
+
+ /** {@inheritDoc} */
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+
+ final FileVisitResult result = super.visitFile(file, attrs);
+ final String fileName = file.getFileName().toString();
+ if (!fileName.endsWith(".jar") ||
+ fileName.endsWith("-tests.jar") ||
+ fileName.endsWith("-sources.jar") ||
+ fileName.endsWith("-javadoc.jar") ) {
+ return result;
+ }
+ final String rootName = fileName.substring(0, fileName.length() - ".jar".length());
+ final Path pomPath = file.getParent().resolve(rootName + ".pom");
+ final ParsedPom pom ;
+ try {
+ pom = new ParsedPom(getProjectContext().getParserPool(), getMavenLoader(), pomPath, "rootName", null, Collections.emptyMap());
+ } catch (Exception e) {
+ log.error("Could not parse pom for {} ", pomPath, e);
+ failCount ++;
+ return result;
+ }
+ if (!checkSignature(file, pom.getOurInfo())) {
+ failCount ++;
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
index b92ca40..a47b712 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/ProjectPomContext.java
@@ -106,6 +106,13 @@ public final class ProjectPomContext implements AutoCloseable {
artifactMap = mapFile;
}
+ /** Returns the parserPool.
+ * @return the parsers
+ */
+ public ParserPool getParserPool() {
+ return parserPool;
+ }
+
/** Get our scratch file system workspace.
* @return {@link #workingDir}
*/
diff --git a/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java b/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java
index f7139b3..4b1e852 100644
--- a/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java
+++ b/src/main/java/net/shibboleth/mvn/enforcer/impl/SigChecker.java
@@ -17,64 +17,37 @@
package net.shibboleth.mvn.enforcer.impl;
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.security.Security;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Optional;
import javax.annotation.Nonnull;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
-import net.shibboleth.mvn.enforcer.impl.GPGKeyRing.Signature;
import net.shibboleth.mvn.enforcer.impl.ParsedPom.PomArtifact;
import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.logic.Constraint;
/**
* A class to iterate over the provided directories for jar files and test
* their signatures.
*/
-public class SigChecker {
+public class SigChecker extends BaseSigChecker {
/** Our log. */
private final Logger log = EnforcerLogger.getLogger(SigChecker.class);
- /** where we are writing to (target/dependencyReport.txt).*/
- private final PrintWriter report;
-
- /** The parent project, suitable digested. */
- private final ProjectPomContext projectContext;
-
- /** The key rings for our signature test. */
- private final Map<String, Optional<GPGKeyRing>> keyRings = new HashMap<>();
-
- /** The injected thing that collects maven artifacts. */
- private final MavenLoader mavenLoader;
-
/** Constructor.
* @param loader How to get artifacts
* @param project The project
- * @param writer Where to write our report
+ * @param report Where to write our report
*/
public SigChecker(@Nonnull final MavenLoader loader,
@Nonnull final ProjectPomContext project,
- @Nonnull final PrintWriter writer) {
- mavenLoader = Constraint.isNotNull(loader, "Loader must not be null");
- projectContext = Constraint.isNotNull(project, "project must not be null");
- report = Constraint.isNotNull(writer, "Writer must not be null");
- if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
+ @Nonnull final PrintWriter report) {
+ super(loader, project, report);
}
/** The Body of the signature test. Are all the files what we expected?
@@ -92,9 +65,9 @@ public class SigChecker {
}
}
if (sigFails != 0) {
- report.format("\t%d non-exempt jar files did not have valid signatures\n", sigFails);
+ getReport().format("\t%d non-exempt jar files did not have valid signatures\n", sigFails);
} else {
- report.format("\tAll non-exempt jar files correctly signed\n");
+ getReport().format("\tAll non-exempt jar files correctly signed\n");
}
return sigFails == 0;
}
@@ -110,121 +83,12 @@ public class SigChecker {
return 0;
}
final Pair<String,String> name = ProjectPomContext.splitFileName(fileName);
- final String group = projectContext.getGroup(name.getFirst());
+ final String group = getProjectContext().getGroup(name.getFirst());
final PomArtifact jarAsArtifact =
- projectContext.getParentPom().new PomArtifact(group, name.getFirst(), name.getSecond());
- if (projectContext.getParentPom().getGeneratedArtifacts().contains(jarAsArtifact)) {
- report.format("%-30s: %-14s Generated by build. Not checked\n", name.getFirst(), name.getSecond());
- return 0;
- }
- if (group == null) {
- report.format("%-30s: %-14s Could not determine group\n", name.getFirst(), name.getSecond());
- log.info("{} {} could not determine group", name.getFirst(), name.getSecond());
- return 1;
- }
- if (projectContext.isSnapShot() && name.getSecond().endsWith("-SNAPSHOT")) {
- report.format("%-30s: %-14s Snapshot version on a snapshot build. Not Checked\n",
- name.getFirst(), name.getSecond());
+ getProjectContext().getParentPom().new PomArtifact(group, name.getFirst(), name.getSecond());
+ if (checkSignature(jarFile, jarAsArtifact)) {
return 0;
}
- final GPGKeyRing keyRing = getKeyRing(group);
- if (keyRing == null) {
- report.format("%-30s: %-14s No keyring for group %s\n", name.getFirst(), name.getSecond(), group);
- log.info("{} {} no keyring for group", name.getFirst(), name.getSecond(), group);
- return 1;
- }
- final Signature sig = getSignature(jarAsArtifact);
- if (sig == null) {
- report.format("%-30s: %-14s Could not find signature (group : %s)\n",
- name.getFirst(), name.getSecond(), group);
- log.info("{} {} could not find signature (group={})", name.getFirst(), name.getSecond(), group);
- return 1;
- }
- if (!keyRing.contains(sig)) {
- report.format("%-30s: %-14s KeyId (%s) not found in keyring for %s\n",
- name.getFirst(), name.getSecond(), sig.toString(), group);
- log.info("{} {} KeyId ({}) not found in keyring for {}",
- name.getFirst(), name.getSecond(), sig.toString(), group);
- return 1;
- }
-
- try (final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(jarFile.toFile()))) {
- if (!keyRing.checkSignature(stream, sig)) {
- report.format("%-30s: %-14s Signature Mismatch : %s in keyring %s\n",
- name.getFirst(), name.getSecond(), keyRing.getKeyInfo(sig), group);
- log.info("{} {} Signature Mismatch : ({}) in keyring for {}",
- name.getFirst(), name.getSecond(), sig.toString(), group);
- return 1;
- }
- } catch (final IOException e) {
- log.error("Failed", e);
- return 1;
- }
- report.format("%-30s: %-14s Signature Match in keyring %s : %s \n",
- name.getFirst(), name.getSecond(), group, keyRing.getKeyInfo(sig));
- return 0;
+ return 1;
}
- // CheckStyle: ReturnCount|CyclomaticComplexity ON
-
- /** Locate and load the signature for this artifact.
- * @param artifact what to load
- * @return the Signature or null if we couldn't locate it.
- */
- private Signature getSignature(final PomArtifact artifact) {
- Path path;
- try {
- path = mavenLoader.downloadArtifact(artifact, "jar.asc");
- } catch (final Exception e) {
- log.debug("Error loading {} from maven loader", artifact, e);
- path = null;
- }
- if (path == null || !Files.exists(path)) {
- log.info("Could not find key for {}, trying local store.", artifact);
- try (final InputStream stream =
- new BufferedInputStream(new FileInputStream(
- projectContext.getEnforcerDir()
- .resolve("localSignatures")
- .resolve( artifact.toString() + ".jar.asc")
- .toFile()))) {
- final Signature sig = GPGKeyRing.signatureOf(stream);
- report.format("%-30s: %-14s Signature not available. Loaded from local store\n",
- artifact.getArtifactId(), artifact.getVersion());
- log.info("Key for {} found in local store.", artifact);
- return sig;
- } catch (final IOException e) {
- log.error("Could not load key from local store:", e);
- return null;
- }
- }
- try (final InputStream stream = new BufferedInputStream(new FileInputStream(path.toFile()))) {
- return GPGKeyRing.signatureOf(stream);
- } catch (final IOException e) {
- log.error("Could not load key from store", e);
- return null;
- }
- }
-
- /** Locate the keyring in the cache or load & cache it (or a negative lookup).
- * @param group the group to load
- * @return a keyring or null if there wasn't one.
- */
- private GPGKeyRing getKeyRing(final String group) {
- final Optional<GPGKeyRing> opt = keyRings.get(group);
- if (opt != null) {
- if (opt.isEmpty()) {
- return null;
- }
- return opt.get();
- }
-
- try {
- final GPGKeyRing store = new GPGKeyRing(projectContext.getEnforcerDir().resolve("keyRings"), group);
- keyRings.put(group, Optional.of(store));
- return store;
- } catch (final Exception e) {
- log.error("Could not load keyring for " + group, e);
- keyRings.put(group, Optional.empty());
- return null;
- }
- }
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list