Skip to content

Commit

Permalink
Merge pull request #15491 from rmuir/forbidden_third_party
Browse files Browse the repository at this point in the history
Add gradle thirdPartyAudit to precommit tasks
  • Loading branch information
rmuir committed Dec 16, 2015
2 parents 207f0a4 + 4213800 commit 4f9d410
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 2 deletions.
10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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())
}
}
8 changes: 8 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions modules/lang-expression/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'

9 changes: 9 additions & 0 deletions modules/lang-groovy/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
11 changes: 11 additions & 0 deletions plugins/discovery-azure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
9 changes: 9 additions & 0 deletions plugins/discovery-ec2/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
3 changes: 3 additions & 0 deletions plugins/discovery-gce/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions plugins/lang-plan-a/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
37 changes: 37 additions & 0 deletions plugins/lang-python/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
7 changes: 7 additions & 0 deletions plugins/mapper-attachments/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
5 changes: 4 additions & 1 deletion plugins/repository-hdfs/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,7 @@ integTest {
cluster {
plugin(pluginProperties.extension.name, zipTree(distZipHadoop2.archivePath))
}
}
}

// classes are missing, e.g. org.mockito.Mockito
thirdPartyAudit.lenient = true
9 changes: 9 additions & 0 deletions plugins/repository-s3/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
11 changes: 11 additions & 0 deletions qa/evil-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
2 changes: 2 additions & 0 deletions test-framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 4f9d410

Please sign in to comment.