Merge pull request #15491 from rmuir/forbidden_third_party

Add gradle thirdPartyAudit to precommit tasks
This commit is contained in:
Robert Muir 2015-12-16 18:56:50 -05:00
commit 4f9d4103f2
16 changed files with 287 additions and 2 deletions

View File

@ -123,6 +123,16 @@ subprojects {
}
}
}
// For reasons we don't fully understand yet, external dependencies are not picked up by Ant's optional tasks.
// But you can easily do it in another way.
// Only if your buildscript and Ant's optional task need the same library would you have to define it twice.
// https://docs.gradle.org/current/userguide/organizing_build_logic.html
configurations {
forbiddenApis
}
dependencies {
forbiddenApis 'de.thetaphi:forbiddenapis:2.0'
}
}
// Ensure similar tasks in dependent projects run first. The projectsEvaluated here is

View File

@ -34,7 +34,8 @@ class PrecommitTasks {
List<Task> precommitTasks = [
configureForbiddenApis(project),
project.tasks.create('forbiddenPatterns', ForbiddenPatternsTask.class),
project.tasks.create('jarHell', JarHellTask.class)]
project.tasks.create('jarHell', JarHellTask.class),
project.tasks.create('thirdPartyAudit', ThirdPartyAuditTask.class)]
// tasks with just tests don't need dependency licenses, so this flag makes adding
// the task optional

View File

@ -0,0 +1,158 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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
*
* 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.elasticsearch.gradle.precommit
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.UnknownConfigurationException
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskAction
import org.apache.tools.ant.BuildLogger
import org.apache.tools.ant.Project
/**
* Basic static checking to keep tabs on third party JARs
*/
public class ThirdPartyAuditTask extends DefaultTask {
// true to be lenient about MISSING CLASSES
private boolean lenient;
// patterns for classes to exclude, because we understand their issues
private String[] excludes = new String[0];
ThirdPartyAuditTask() {
dependsOn(project.configurations.testCompile)
description = "Checks third party JAR bytecode for missing classes, use of internal APIs, and other horrors'"
}
/**
* Set to true to be lenient with dependencies. By default this check will fail if it finds
* MISSING CLASSES. This means the set of jars is incomplete. However, in some cases
* this can be due to intentional exclusions that are well-tested and understood.
*/
public void setLenient(boolean value) {
lenient = value;
}
/**
* Returns true if leniency about missing classes is enabled.
*/
public boolean isLenient() {
return lenient;
}
/**
* classes that should be excluded from the scan,
* e.g. because we know what sheisty stuff those particular classes are up to.
*/
public void setExcludes(String[] classes) {
for (String s : classes) {
if (s.indexOf('*') != -1) {
throw new IllegalArgumentException("illegal third party audit exclusion: '" + s + "', wildcards are not permitted!")
}
}
excludes = classes;
}
/**
* Returns current list of exclusions.
*/
public String[] getExcludes() {
return excludes;
}
@TaskAction
public void check() {
AntBuilder ant = new AntBuilder()
// we are noisy for many reasons, working around performance problems with forbidden-apis, dealing
// with warnings about missing classes, etc. so we use our own "quiet" AntBuilder
ant.project.buildListeners.each { listener ->
if (listener instanceof BuildLogger) {
listener.messageOutputLevel = Project.MSG_ERR;
}
};
// we only want third party dependencies.
FileCollection jars = project.configurations.testCompile.fileCollection({ dependency ->
dependency.group != "org.elasticsearch"
})
// we don't want provided dependencies, which we have already scanned. e.g. don't
// scan ES core's dependencies for every single plugin
try {
jars -= project.configurations.getByName("provided")
} catch (UnknownConfigurationException ignored) {}
// no dependencies matched, we are done
if (jars.isEmpty()) {
return;
}
ant.taskdef(name: "thirdPartyAudit",
classname: "de.thetaphi.forbiddenapis.ant.AntTask",
classpath: project.configurations.forbiddenApis.asPath)
// print which jars we are going to scan, always
// this is not the time to try to be succinct! Forbidden will print plenty on its own!
Set<String> names = new HashSet<>()
for (File jar : jars) {
names.add(jar.getName())
}
logger.error("[thirdPartyAudit] Scanning: " + names)
// warn that you won't see any forbidden apis warnings
if (lenient) {
logger.warn("[thirdPartyAudit] WARNING: leniency is enabled, will not fail if classes are missing!")
}
// TODO: forbidden-apis + zipfileset gives O(n^2) behavior unless we dump to a tmpdir first,
// and then remove our temp dir afterwards. don't complain: try it yourself.
// we don't use gradle temp dir handling, just google it, or try it yourself.
File tmpDir = new File(project.buildDir, 'tmp/thirdPartyAudit')
// clean up any previous mess (if we failed), then unzip everything to one directory
ant.delete(dir: tmpDir.getAbsolutePath())
tmpDir.mkdirs()
for (File jar : jars) {
ant.unzip(src: jar.getAbsolutePath(), dest: tmpDir.getAbsolutePath())
}
// convert exclusion class names to binary file names
String[] excludedFiles = new String[excludes.length];
for (int i = 0; i < excludes.length; i++) {
excludedFiles[i] = excludes[i].replace('.', '/') + ".class"
// check if the excluded file exists, if not, sure sign things are outdated
if (! new File(tmpDir, excludedFiles[i]).exists()) {
throw new IllegalStateException("bogus thirdPartyAudit exclusion: '" + excludes[i] + "', not found in any dependency")
}
}
ant.thirdPartyAudit(internalRuntimeForbidden: true,
failOnUnsupportedJava: false,
failOnMissingClasses: !lenient,
classpath: project.configurations.testCompile.asPath) {
fileset(dir: tmpDir, excludes: excludedFiles.join(','))
}
// clean up our mess (if we succeed)
ant.delete(dir: tmpDir.getAbsolutePath())
}
}

View File

@ -111,6 +111,14 @@ forbiddenPatterns {
exclude '**/org/elasticsearch/cluster/routing/shard_routes.txt'
}
// classes are missing, e.g. org.jboss.marshalling.Marshaller
thirdPartyAudit.lenient = true
// uses internal sun ssl classes!
thirdPartyAudit.excludes = [
// uses internal java api: sun.security.x509 (X509CertInfo, X509CertImpl, X500Name)
'org.jboss.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator',
]
// dependency license are currently checked in distribution
dependencyLicenses.enabled = false

View File

@ -33,6 +33,10 @@ dependencyLicenses {
mapping from: /lucene-.*/, to: 'lucene'
}
// do we or do we not depend on asm-tree, that is the question
// classes are missing, e.g. org.objectweb.asm.tree.LabelNode
thirdPartyAudit.lenient = true
compileJava.options.compilerArgs << '-Xlint:-rawtypes'
compileTestJava.options.compilerArgs << '-Xlint:-rawtypes'

View File

@ -35,3 +35,12 @@ integTest {
systemProperty 'es.script.indexed', 'on'
}
}
// classes are missing, e.g. jline.console.completer.Completer
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: sun.misc.Unsafe
'groovy.json.internal.FastStringUtils',
'groovy.json.internal.FastStringUtils$StringImplementation$1',
'groovy.json.internal.FastStringUtils$StringImplementation$2',
]

View File

@ -66,3 +66,14 @@ compileJava.options.compilerArgs << '-Xlint:-deprecation'
// TODO: and why does this static not show up in maven...
compileTestJava.options.compilerArgs << '-Xlint:-static'
// classes are missing, e.g. org.osgi.framework.BundleActivator
thirdPartyAudit.lenient = true
// WE ARE JAR HELLING WITH THE JDK AND THAT IS WHY THIS HAPPENS
// TODO: fix this!!!!!!!!!!!
thirdPartyAudit.excludes = [
// uses internal java api: com.sun.xml.fastinfoset.stax.StAXDocumentParser
'com.sun.xml.bind.v2.runtime.unmarshaller.FastInfosetConnector',
'com.sun.xml.bind.v2.runtime.unmarshaller.FastInfosetConnector$CharSequenceImpl',
// uses internal java api: com.sun.xml.fastinfoset.stax.StAXDocumentSerializer
'com.sun.xml.bind.v2.runtime.output.FastInfosetStreamWriterOutput',
]

View File

@ -48,3 +48,12 @@ test {
// this is needed for insecure plugins, remove if possible!
systemProperty 'tests.artifact', project.name
}
// classes are missing, e.g. org.apache.avalon.framework.logger.Logger
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
// uses internal java api: com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault
// uses internal java api: com.sun.org.apache.xpath.internal.XPathContext
'com.amazonaws.util.XpathUtils',
]

View File

@ -31,3 +31,6 @@ test {
// this is needed for insecure plugins, remove if possible!
systemProperty 'tests.artifact', project.name
}
// classes are missing, e.g. org.apache.log.Logger
thirdPartyAudit.lenient = true

View File

@ -33,6 +33,9 @@ dependencies {
compileJava.options.compilerArgs << '-Xlint:-cast,-fallthrough,-rawtypes'
compileTestJava.options.compilerArgs << '-Xlint:-unchecked'
// classes are missing, e.g. org.objectweb.asm.tree.LabelNode
thirdPartyAudit.lenient = true
// regeneration logic, comes in via ant right now
// don't port it to gradle, it works fine.

View File

@ -36,3 +36,40 @@ integTest {
}
}
// classes are missing, e.g. org.tukaani.xz.FilterOptions
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: sun.security.x509 (X509CertInfo, X509CertImpl, X500Name)
'org.python.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator',
// uses internal java api: sun.misc.Cleaner
'org.python.netty.util.internal.Cleaner0',
// uses internal java api: sun.misc.Signal
'jnr.posix.JavaPOSIX',
'jnr.posix.JavaPOSIX$SunMiscSignalHandler',
// uses internal java api: sun.misc.Unsafe
'com.kenai.jffi.MemoryIO$UnsafeImpl',
'com.kenai.jffi.MemoryIO$UnsafeImpl32',
'com.kenai.jffi.MemoryIO$UnsafeImpl64',
'org.python.google.common.cache.Striped64',
'org.python.google.common.cache.Striped64$1',
'org.python.google.common.cache.Striped64$Cell',
'org.python.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator',
'org.python.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1',
'org.python.netty.util.internal.chmv8.ForkJoinPool$2',
'org.python.netty.util.internal.PlatformDependent0',
'org.python.netty.util.internal.UnsafeAtomicIntegerFieldUpdater',
'org.python.netty.util.internal.UnsafeAtomicLongFieldUpdater',
'org.python.netty.util.internal.UnsafeAtomicReferenceFieldUpdater',
'org.python.netty.util.internal.chmv8.ConcurrentHashMapV8',
'org.python.netty.util.internal.chmv8.ConcurrentHashMapV8$1',
'org.python.netty.util.internal.chmv8.ConcurrentHashMapV8$TreeBin',
'org.python.netty.util.internal.chmv8.CountedCompleter',
'org.python.netty.util.internal.chmv8.CountedCompleter$1',
'org.python.netty.util.internal.chmv8.ForkJoinPool',
'org.python.netty.util.internal.chmv8.ForkJoinPool$WorkQueue',
'org.python.netty.util.internal.chmv8.ForkJoinTask',
'org.python.netty.util.internal.chmv8.ForkJoinTask$1',
]

View File

@ -69,3 +69,10 @@ forbiddenPatterns {
exclude '**/*.pdf'
exclude '**/*.epub'
}
// classes are missing, e.g. org.openxmlformats.schemas.drawingml.x2006.chart.CTExtensionList
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: com.sun.syndication (SyndFeedInput, SyndFeed, SyndEntry, SyndContent)
'org.apache.tika.parser.feed.FeedParser',
]

View File

@ -201,3 +201,6 @@ integTest {
plugin(pluginProperties.extension.name, zipTree(distZipHadoop2.archivePath))
}
}
// classes are missing, e.g. org.mockito.Mockito
thirdPartyAudit.lenient = true

View File

@ -49,3 +49,12 @@ test {
// this is needed for insecure plugins, remove if possible!
systemProperty 'tests.artifact', project.name
}
// classes are missing, e.g. org.apache.log.Logger
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
// uses internal java api: com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault
// uses internal java api: com.sun.org.apache.xpath.internal.XPathContext
'com.amazonaws.util.XpathUtils',
]

View File

@ -34,3 +34,14 @@ dependencies {
test {
systemProperty 'tests.security.manager', 'false'
}
// classes are missing, com.ibm.icu.lang.UCharacter
thirdPartyAudit.lenient = true
thirdPartyAudit.excludes = [
// uses internal java api: sun.misc.Unsafe
'com.google.common.cache.Striped64',
'com.google.common.cache.Striped64$1',
'com.google.common.cache.Striped64$Cell',
'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator',
'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1',
]

View File

@ -47,3 +47,5 @@ forbiddenApisMain {
// TODO: should we have licenses for our test deps?
dependencyLicenses.enabled = false
// we intentionally exclude the ant tasks because people were depending on them from their tests!!!!!!!
thirdPartyAudit.lenient = true