[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