Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
common/build.gradle
### 代码内容：
description = 'GoCD Common Module'

dependencies {
  api project(':config:config-api')
  api project(':commandline')
  api project(':base')
  api project(':util')
  api project(':domain')
  api project(':plugin-infra:go-plugin-access')
  api project.deps.dom4j
  api project.deps.apacheHttpMime
  api project.deps.commonsCollections4
  api project.deps.springTx
  implementation project.deps.jodaTime
  annotationProcessor project.deps.lombok
  compileOnly project.deps.jetBrainsAnnotations

  testImplementation project.deps.bouncyCastle
  testImplementation project.deps.bouncyCastlePkix
  testImplementation project(path: ':config:config-api', configuration: 'testOutput')
  testImplementation project(path: ':domain', configuration: 'testOutput')
  testImplementation project.deps.jsonUnit
  testImplementation project(':test:test-utils')
  testImplementation project.deps.guava
  testImplementation project.deps.junit5
  testRuntimeOnly project.deps.junit5Engine
  testImplementation project.deps.junit5Params
  testImplementation project.deps.mockitoJunit5
}

## 内容2
### 文件路径：
common/src/main/java/com/thoughtworks/go/domain/DirectoryEntries.java
### 代码内容：
package com.thoughtworks.go.domain;

import com.thoughtworks.go.CurrentGoCDVersion;
import com.thoughtworks.go.server.presentation.html.HtmlElement;
import com.thoughtworks.go.server.presentation.html.HtmlRenderable;
import com.thoughtworks.go.server.presentation.models.HtmlRenderer;
import com.thoughtworks.go.util.json.JsonAware;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.thoughtworks.go.server.presentation.html.HtmlElement.p;

public class DirectoryEntries extends ArrayList<DirectoryEntry> implements HtmlRenderable, JsonAware {
    private boolean isArtifactsDeleted;

    @Override
    public void render(HtmlRenderer renderer) {
        if (isArtifactsDeleted || isEmpty()) {
            HtmlElement element = p().content("Artifacts for this job instance are unavailable as they may have been purged by Go or deleted externally. "
                    + "Re-run the stage or job to generate them again.");
            element.render(renderer);
        }
        for (DirectoryEntry entry : this) {
            entry.toHtml().render(renderer);
        }
    }

    @Override
    public List<Map<String, Object>> toJson() {
        List<Map<String, Object>> jsonList = new ArrayList();
        for (DirectoryEntry entry : this) {
            jsonList.add(entry.toJson());
        }
        return jsonList;
    }

    public boolean isArtifactsDeleted() {
        return isArtifactsDeleted;
    }

    public void setIsArtifactsDeleted(boolean artifactsDeleted) {
        isArtifactsDeleted = artifactsDeleted;
    }

    public FolderDirectoryEntry addFolder(String folderName) {
        FolderDirectoryEntry folderDirectoryEntry = new FolderDirectoryEntry(folderName, "", new DirectoryEntries());
        add(folderDirectoryEntry);
        return folderDirectoryEntry;
    }

    public void addFile(String fileName, String url) {
        add(new FileDirectoryEntry(fileName, url));
    }
}

## 内容3
### 文件路径：
common/src/main/java/com/thoughtworks/go/domain/FileDirectoryEntry.java
### 代码内容：
package com.thoughtworks.go.domain;

import com.thoughtworks.go.server.presentation.html.HtmlRenderable;
import com.thoughtworks.go.server.presentation.html.HtmlAttribute;
import com.thoughtworks.go.server.presentation.html.HtmlElement;

public class FileDirectoryEntry extends DirectoryEntry {

    public FileDirectoryEntry(String fileName, String url) {
        super(fileName, url, "file");
    }

    @Override
    protected HtmlRenderable htmlBody() {
        return HtmlElement.li().content(
            HtmlElement.span(HtmlAttribute.cssClass("artifact")).content(
                HtmlElement.a(HtmlAttribute.href(getUrl()))
                        .content(getFileName())
            )
        );

    }
}

## 内容4
### 文件路径：
common/src/main/java/com/thoughtworks/go/domain/FolderDirectoryEntry.java
### 代码内容：
package com.thoughtworks.go.domain;

import com.thoughtworks.go.server.presentation.html.HtmlElement;
import com.thoughtworks.go.server.presentation.html.HtmlRenderable;

import java.util.Map;

import static com.thoughtworks.go.server.presentation.html.HtmlAttribute.*;
import static com.thoughtworks.go.server.presentation.html.ListedElements.sequence;

public class FolderDirectoryEntry extends DirectoryEntry {
    private final DirectoryEntries subDirectory;

    public FolderDirectoryEntry(String fileName, String url, DirectoryEntries subDirectory) {
        super(fileName, url, "folder");
        this.subDirectory = subDirectory;
    }

    public DirectoryEntries getSubDirectory() {
        return subDirectory;
    }

    @Override
    protected HtmlRenderable htmlBody() {
        return sequence(
                HtmlElement.div(cssClass("dir-container")).content(
                    HtmlElement.span(cssClass("directory")).content(
                        HtmlElement.a(onclick("BuildDetail.tree_navigator(this)"))
                                .content(getFileName())
                    )
                ),
                HtmlElement.div(cssClass("subdir-container"), style("display:none"))
                        .content(subDirectory)
        );
    }

    @Override
    public Map<String, Object> toJson() {
        Map json = super.toJson();
        json.put("files", subDirectory.toJson());
        return json;
    }

    public void addFile(String fileName, String url) {
        subDirectory.addFile(fileName, url);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        FolderDirectoryEntry that = (FolderDirectoryEntry) o;

        if (!subDirectory.equals(that.subDirectory)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return subDirectory.hashCode();
    }
}

## 内容5
### 文件路径：
common/src/main/java/com/thoughtworks/go/server/presentation/html/HtmlElement.java
### 代码内容：
package com.thoughtworks.go.server.presentation.html;

import java.util.ArrayList;
import java.util.List;

import com.thoughtworks.go.server.presentation.models.HtmlRenderer;

public class HtmlElement implements HtmlRenderable {
    public static HtmlElement div(HtmlAttribute... attributes) { return new HtmlElement("div", attributes); }
    public static HtmlElement span(HtmlAttribute... attributes) { return new HtmlElement("span", attributes); }
    public static HtmlElement a(HtmlAttribute... attributes) { return new HtmlElement("a", attributes); }
    public static HtmlElement p(HtmlAttribute... attributes) { return new HtmlElement("p", attributes); }
    public static HtmlElement ul(HtmlAttribute... attributes) { return new HtmlElement("ul", attributes); }
    public static HtmlElement li(HtmlAttribute... attributes) { return new HtmlElement("li", attributes); }

    private final String elementName;
    private final HtmlAttribute[] attributes;
    private final List<HtmlRenderable> elements = new ArrayList<>();

    private HtmlElement(String elementName, HtmlAttribute... attributes) {
        this.elementName = elementName;
        this.attributes = attributes;
    }

    public HtmlElement content(String body) {
        return content(new TextElement(body));
    }

    public HtmlElement content(HtmlRenderable... elements) {
        for (HtmlRenderable element : elements) {
            addToBody(element);
        }
        return this;
    }

    public HtmlElement content(List<? extends Htmlable> htmlables) {
        for (Htmlable htmlable : htmlables) {
            addToBody(htmlable.toHtml());
        }
        return this;
    }

    public HtmlElement addToBody(HtmlRenderable element) {
        this.elements.add(element);
        return this;
    }

    @Override
    public void render(HtmlRenderer renderer) {
        renderer.append("<" + elementName);

        for (HtmlAttribute attribute : attributes) {
            attribute.render(renderer);
        }

        if (elements.isEmpty()) {
            renderer.append(" />\n");
        } else {
            renderer.append(">\n");

            for (HtmlRenderable element : elements) {
                element.render(renderer);
            }

            renderer.append("</" + elementName + ">\n");
        }
    }

}

## 内容6
### 文件路径：
dependencies.gradle
### 代码内容：
/** Parses a version from a dependency declaration string */
static String versionOf(String dependencySpec) {
  return dependencySpec.split(':').last()
}

final Map<String, String> libraries = [
  // Dependabot will parse these dependencies.
  // Keep all of these as uninterpolated string literals so that Dependabot can parse the versions and create PRs for
  // upgrades.
  //
  // DO NOT interpolate version variables here because Dependabot is not smart enough to understand those. Dependabot's
  // version parsing is simply regex matching and never actually evaluates a gradle script.
  activeMQ            : 'org.apache.activemq:activemq-broker:5.16.3',
  apacheAnt           : 'org.apache.ant:ant:1.10.11',
  apacheHttpComponents: 'org.apache.httpcomponents:httpclient:4.5.13',
  aspectj             : 'org.aspectj:aspectjrt:1.9.7',
  assertJ             : 'org.assertj:assertj-core:3.21.0',
  assertJ_DB          : 'org.assertj:assertj-db:2.0.2',
  bouncyCastle        : 'org.bouncycastle:bcprov-jdk15on:1.68', // This version of BC is not compatible with the jruby-opensssl version, since we are not using jruby-opensssl going ahead with the upgrade.
  bundler             : 'rubygems:bundler:2.1.1',
  cglib               : 'cglib:cglib:3.3.0',
  cloning             : 'io.github.kostaskougios:cloning:1.10.3',
  commonsBeanUtils    : 'commons-beanutils:commons-beanutils:1.9.4',
  commonsCodec        : 'commons-codec:commons-codec:1.15',
  commonsCollections  : 'commons-collections:commons-collections:3.2.2',
  commonsCollections4 : 'org.apache.commons:commons-collections4:4.4',
  commonsConfiguration: 'org.apache.commons:commons-configuration2:2.7',
  commonsDbcp         : 'org.apache.commons:commons-dbcp2:2.9.0',
  commonsDigester     : 'commons-digester:commons-digester:2.1',
  commonsFileUpload   : 'commons-fileupload:commons-fileupload:1.4',
  commonsIO           : 'commons-io:commons-io:2.11.0',
  commonsLang         : 'commons-lang:commons-lang:2.6',
  commonsLang3        : 'org.apache.commons:commons-lang3:3.12.0',
  commonsPool         : 'org.apache.commons:commons-pool2:2.11.1',
  dbunit              : 'org.dbunit:dbunit:2.7.2',
  dom4j               : 'dom4j:dom4j:1.6.1',
  ehcache             : 'net.sf.ehcache:ehcache:2.10.9.2',
  felix               : 'org.apache.felix:org.apache.felix.framework:5.6.12',
  freemarker          : 'org.freemarker:freemarker:2.3.31',
  gradleDownload      : 'de.undercouch:gradle-download-task:4.1.2',
  grolifant           : 'org.ysb33r.gradle:grolifant60:1.3.1',
  gson                : 'com.google.code.gson:gson:2.8.8',
  guava               : 'com.google.guava:guava:31.0.1-jre',
  h2                  : 'com.h2database:h2:1.4.200',
  hamcrest            : 'org.hamcrest:hamcrest-core:2.2',
  hibernate           : 'org.hibernate:hibernate-ehcache:3.6.10.Final',
  httpClientMock      : 'com.github.paweladamski:HttpClientMock:1.10.0',
  jackson             : 'com.fasterxml.jackson.core:jackson-core:2.13.0',
  javaAssist          : 'javassist:javassist:3.12.1.GA',
  javaxAnnotation     : 'javax.annotation:javax.annotation-api:1.3.2',
  jaxb                : 'javax.xml.bind:jaxb-api:2.3.1',
  jaxen               : 'jaxen:jaxen:1.2.0',
  jcommander          : 'com.beust:jcommander:1.81',
  jdom                : 'org.jdom:jdom2:2.0.6',
  jetBrainsAnnotations: 'org.jetbrains:annotations:22.0.0',
  jetty               : 'org.eclipse.jetty:jetty-server:9.4.35.v20201120',
  jgit                : 'org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r',
  jodaTime            : 'joda-time:joda-time:2.10.12', // joda-time version has to be compatible with the jruby version
  jolt                : 'com.bazaarvoice.jolt:jolt-core:0.1.5',
  jruby               : 'org.jruby:jruby-complete:9.2.0.0',
  jsonUnit            : 'net.javacrumbs.json-unit:json-unit-fluent:2.28.0',
  jsontools           : 'com.sdicons.jsontools:jsontools-core:1.7',
  jsoup               : 'org.jsoup:jsoup:1.14.3',
  junit5              : 'org.junit.jupiter:junit-jupiter-api:5.8.1',
  liquibase           : 'org.liquibase:liquibase-core:4.4.3',
  logback             : 'ch.qos.logback:logback-classic:1.2.6',
  lombok              : 'org.projectlombok:lombok:1.18.22',
  mail                : 'com.sun.mail:mailapi:1.6.1',
  mockito             : 'org.mockito:mockito-core:4.0.0',
  mybatis             : 'org.mybatis:mybatis:3.5.7',
  mybatisSpring       : 'org.mybatis:mybatis-spring:2.0.6',
  mysql               : 'mysql:mysql-connector-java:8.0.27',
  nanohttpd           : 'org.nanohttpd:nanohttpd:2.3.1',
  objenesis           : 'org.objenesis:objenesis:3.2',
  oscache             : 'opensymphony:oscache:2.4.1',
  postgresql          : 'org.postgresql:postgresql:42.3.0',
  quartz              : 'org.quartz-scheduler:quartz:2.3.2',
  rack                : 'org.jruby.rack:jruby-rack:1.1.21',
  semanticVersion     : 'de.skuzzle:semantic-version:2.1.1',
  servletApi          : 'javax.servlet:javax.servlet-api:4.0.1',
  slf4j               : 'org.slf4j:slf4j-api:1.7.32',
  spark               : 'com.sparkjava:spark-core:2.7.2',
  spring              : 'org.springframework:spring-core:4.3.30.RELEASE',
  springSecurity      : 'org.springframework.security:spring-security-config:4.2.20.RELEASE',
  springTestJunit5    : 'com.github.sbrannen:spring-test-junit5:1.5.0',
  systemStubs         : 'uk.org.webcompere:system-stubs-jupiter:1.2.0',
  testcontainers      : 'org.testcontainers:testcontainers:1.16.0',
  tinybundles         : 'org.ops4j.pax.tinybundles:tinybundles:3.0.0',
  tokenBucket         : 'org.isomorphism:token-bucket:1.7',
  urlrewrite          : 'org.tuckey:urlrewritefilter:3.2.0',
  velocity            : 'org.apache.velocity:velocity:1.7',
  xalan               : 'xalan:xalan:2.7.2',
  xmlUnit             : 'org.xmlunit:xmlunit-assertj:2.8.3',
  ztExec              : 'org.zeroturnaround:zt-exec:1.12',
]

// Export versions that are needed outside of this file (and elsewhere within)
final Map<String, String> v = [
  activeMQ            : versionOf(libraries.activeMQ),
  apacheAnt           : versionOf(libraries.apacheAnt),
  apacheHttpComponents: versionOf(libraries.apacheHttpComponents),
  aspectj             : versionOf(libraries.aspectj),
  bouncyCastle        : versionOf(libraries.bouncyCastle),
  cglib               : versionOf(libraries.cglib),
  cloning             : versionOf(libraries.cloning),
  commonsBeanutils    : versionOf(libraries.commonsBeanUtils),
  commonsCodec        : versionOf(libraries.commonsCodec),
  commonsCollections  : versionOf(libraries.commonsCollections),
  commonsCollections4 : versionOf(libraries.commonsCollections4),
  commonsConfiguration: versionOf(libraries.commonsConfiguration),
  commonsDbcp         : versionOf(libraries.commonsDbcp),
  commonsDigester     : versionOf(libraries.commonsDigester),
  commonsFileUpload   : versionOf(libraries.commonsFileUpload),
  commonsIO           : versionOf(libraries.commonsIO),
  commonsLang         : versionOf(libraries.commonsLang),
  commonsLang3        : versionOf(libraries.commonsLang3),
  commonsPool         : versionOf(libraries.commonsPool),
  dom4j               : versionOf(libraries.dom4j),
  ehcache             : versionOf(libraries.ehcache),
  felix               : versionOf(libraries.felix),
  freemarker          : versionOf(libraries.freemarker),
  gson                : versionOf(libraries.gson),
  guava               : versionOf(libraries.guava),
  h2                  : versionOf(libraries.h2),
  hamcrest            : versionOf(libraries.hamcrest),
  hibernate           : versionOf(libraries.hibernate),
  jackson             : versionOf(libraries.jackson),
  javaAssist          : versionOf(libraries.javaAssist),
  javaxAnnotation     : versionOf(libraries.javaxAnnotation),
  jaxb                : versionOf(libraries.jaxb),
  jaxen               : versionOf(libraries.jaxen),
  jcommander          : versionOf(libraries.jcommander),
  jdom                : versionOf(libraries.jdom),
  jetty               : versionOf(libraries.jetty),
  jgit                : versionOf(libraries.jgit),
  jodaTime            : versionOf(libraries.jodaTime),
  jolt                : versionOf(libraries.jolt),
  jruby               : versionOf(libraries.jruby),
  junit5              : versionOf(libraries.junit5),
  liquibase           : versionOf(libraries.liquibase),
  logback             : versionOf(libraries.logback),
  mail                : versionOf(libraries.mail),
  mockito             : versionOf(libraries.mockito),
  mybatis             : versionOf(libraries.mybatis),
  mybatisSpring       : versionOf(libraries.mybatisSpring),
  mysql               : versionOf(libraries.mysql),
  nanohttpd           : versionOf(libraries.nanohttpd),
  objenesis           : versionOf(libraries.objenesis),
  oscache             : versionOf(libraries.oscache),
  postgresql          : versionOf(libraries.postgresql),
  quartz              : versionOf(libraries.quartz),
  rack                : versionOf(libraries.rack),
  semanticVersion     : versionOf(libraries.semanticVersion),
  servletApi          : versionOf(libraries.servletApi),
  slf4j               : versionOf(libraries.slf4j),
  spark               : versionOf(libraries.spark),
  spring              : versionOf(libraries.spring),
  springSecurity      : versionOf(libraries.springSecurity),
  testcontainers      : versionOf(libraries.testcontainers),
  tokenBucket         : versionOf(libraries.tokenBucket),
  urlrewrite          : versionOf(libraries.urlrewrite),
  velocity            : versionOf(libraries.velocity),
  xmlUnit             : versionOf(libraries.xmlUnit),
  ztExec              : versionOf(libraries.ztExec),

  // misc
  tanuki              : '3.5.41',
  tini                : '0.18.0',
  velocityToolsView   : '1.4',
]

// While Dependabot won't be able to parse these deps, these will get upgraded for free anyway since they share versions
// with dependencies declared above that are parseable by Dependabot. This is just a workaround to be DRY while still
// benefiting from Dependabot's automatic PR upgrades.
final Map<String, String> related = [
  apacheHttpMime          : "org.apache.httpcomponents:httpmime:${v.apacheHttpComponents}",
  aspectjWeaver           : "org.aspectj:aspectjweaver:${v.aspectj}",
  bouncyCastlePkix        : "org.bouncycastle:bcpkix-jdk15on:${v.bouncyCastle}",
  hamcrestLibrary         : "org.hamcrest:hamcrest-library:${v.hamcrest}",
  jacksonDatabind         : "com.fasterxml.jackson.core:jackson-databind:${v.jackson}",
  jaxbRuntime             : "org.glassfish.jaxb:jaxb-runtime:${v.jaxb}",
  jettyDeploy             : "org.eclipse.jetty:jetty-deploy:${v.jetty}",
  jettyJmx                : "org.eclipse.jetty:jetty-jmx:${v.jetty}",
  jettyPlus               : "org.eclipse.jetty:jetty-plus:${v.jetty}",
  jettyServlet            : "org.eclipse.jetty:jetty-servlet:${v.jetty}",
  jettyServlets           : "org.eclipse.jetty:jetty-servlets:${v.jetty}",
  jettyUtil               : "org.eclipse.jetty:jetty-util:${v.jetty}",
  jettyWebapp             : "org.eclipse.jetty:jetty-webapp:${v.jetty}",
  jettyWebsocket          : "org.eclipse.jetty.websocket:websocket-server:${v.jetty}",
  jgitServer              : "org.eclipse.jgit:org.eclipse.jgit.http.server:${v.jgit}",
  joltJsonUtils           : "com.bazaarvoice.jolt:json-utils:${v.jolt}",
  junit5Engine            : "org.junit.jupiter:junit-jupiter-engine:${v.junit5}",
  junit5Params            : "org.junit.jupiter:junit-jupiter-params:${v.junit5}",
  mockitoJunit5           : "org.mockito:mockito-junit-jupiter:${v.mockito}",
  mailSmtp                : "com.sun.mail:smtp:${v.mail}",
  slf4jJcl                : "org.slf4j:jcl-over-slf4j:${v.slf4j}",
  slf4jJul                : "org.slf4j:jul-to-slf4j:${v.slf4j}",
  slf4jLog4j              : "org.slf4j:log4j-over-slf4j:${v.slf4j}",
  springContext           : "org.springframework:spring-context:${v.spring}",
  springContextSupport    : "org.springframework:spring-context-support:${v.spring}",
  springOrm               : "org.springframework:spring-orm:${v.spring}",
  springSecurityWeb       : "org.springframework.security:spring-security-web:${v.springSecurity}",
  springTest              : "org.springframework:spring-test:${v.spring}",
  springTx                : "org.springframework:spring-tx:${v.spring}",
  springWeb               : "org.springframework:spring-web:${v.spring}",
  springWebmvc            : "org.springframework:spring-webmvc:${v.spring}",
  testcontainersJdbc      : "org.testcontainers:jdbc:${v.testcontainers}",
  testcontainersJunit     : "org.testcontainers:junit-jupiter:${v.testcontainers}",
  testcontainersMysql     : "org.testcontainers:mysql:${v.testcontainers}",
  testcontainersPostgresql: "org.testcontainers:postgresql:${v.testcontainers}",
  xmlUnitMatchers         : "org.xmlunit:xmlunit-matchers:${v.xmlUnit}",
]

ext {
  //noinspection GroovyAssignabilityCheck
  deps = libraries + related
  //noinspection GroovyAssignabilityCheck
  versions = v
}

## 内容7
### 文件路径：
server/build.gradle
### 代码内容：
import com.github.jk1.license.task.ReportTask
import com.thoughtworks.go.build.ExecuteUnderRailsTask
import com.thoughtworks.go.build.LicenseReport
import com.thoughtworks.go.build.VerifyJarTask
import groovy.json.JsonSlurper
import org.eclipse.jgit.api.Git
import org.gradle.util.TextUtil

import java.nio.file.FileSystems

import static org.jruby.runtime.Constants.RUBY_VERSION

description = 'GoCD Server'

buildscript {
  repositories {
    mavenCentral()
    maven { url 'https://jitpack.io' }
  }

  dependencies {
    classpath project.deps.jruby
    classpath project.deps.jgit
  }
}
apply plugin: 'com.github.jruby-gradle.core'

repositories {
  ruby.gems()
  maven { url 'https://jitpack.io' }
}

sourceSets {
  sharedTest {
    java {
      srcDirs = [project.file('src/test-shared/java')]
    }
    resources {
      srcDirs = [project.file('src/test-shared/resources')]
    }

    compileClasspath += sourceSets.main.output + configurations.compileClasspath + configurations.testCompileClasspath
    runtimeClasspath += sourceSets.main.output + configurations.compileClasspath + configurations.testCompileClasspath
  }

  fastUnitTest {
    java {
      srcDirs = [project.file('src/test-fast/java')]
    }
    resources {
      srcDirs = [project.file('src/test-fast/resources')]
    }

    compileClasspath += sourceSets.sharedTest.output + sourceSets.sharedTest.compileClasspath
    runtimeClasspath += sourceSets.sharedTest.output + sourceSets.sharedTest.runtimeClasspath
  }

  integrationTest {
    java {
      srcDirs = [project.file('src/test-integration/java')]
    }
    resources {
      srcDirs = [project.file('src/test-integration/resources')]
    }

    compileClasspath += sourceSets.sharedTest.output + sourceSets.sharedTest.compileClasspath
    runtimeClasspath += sourceSets.sharedTest.output + sourceSets.sharedTest.runtimeClasspath
  }
}

configurations {
  jrubyGems

  integrationTestImplementation.extendsFrom testImplementation

  warConfig
  jarConfig
  additionalClasspath

  copyOnly {
    transitive = false
  }

  copyOnlyTestData {
    transitive = false
  }

  sharedTestOutput {
    extendsFrom testRuntimeClasspath
    transitive = true
  }

  compileClasspath.exclude(group: 'org.springframework', module: 'spring-support')
  runtimeClasspath.exclude(group: 'org.springframework', module: 'spring-support')
  apiBase
  allApis
  spark
}

project.ext.webAppDir = file("src/main/webapp")
project.ext.railsRoot = file("${project.webAppDir}/WEB-INF/rails")
project.ext.bundledGemDir = file("${project.railsRoot}/gems/jruby/${RUBY_VERSION}")

project.ext.railsClasspath = project.sourceSets.test.runtimeClasspath +
  project.sourceSets.main.runtimeClasspath +
  project.sourceSets.sharedTest.runtimeClasspath +
  project.sourceSets.fastUnitTest.runtimeClasspath +
  project.configurations.spark +
  project.configurations.apiBase +
  project.rootProject.findProject(':test:test-utils').files("resource-include-in-all-projects")

project.ext.jrubyOptimizationJvmArgs = [
  '-XX:+TieredCompilation',
  '-XX:TieredStopAtLevel=1',
  '-Xss2048k',
  '-client'
]

project.ext.jrubyOptimizationSystemProperties = [
  'jruby.compile.mode'         : 'OFF',
  'jruby.compile.invokedynamic': false
]
project.ext.jrubyDefaultSystemProperties = [
  'jruby.home': 'uri:classloader://META-INF/jruby.home',
  // turn these on for debugging some stuff with jruby not finding scripts
  // 'jruby.debug.scriptResolution': 'true',
  // 'jruby.debug.loadService': 'true',
]

project.ext.jrubyJar = { project.configurations.compileClasspath.files { it.name == 'jruby-complete' }.first() }

project.ext.jrubyScriptsDir = project.file("scripts")

project.ext.additionalJRubyPaths = [jrubyScriptsDir, project.file("${project.bundledGemDir}/bin")]

project.ext.defaultJRubyEnvironment = [
  GEM_HOME: project.bundledGemDir,
  GEM_PATH: project.bundledGemDir,
  RUBY    : project.file("${project.ext.jrubyScriptsDir}/jruby${org.gradle.internal.os.OperatingSystem.current().isWindows() ? '.bat' : ''}")
]

project.ext.rails = [:]
project.ext.rails.testDataDir = project.file("${project.buildDir}/railsTests")
project.ext.rails.testConfigDir = project.file("${project.buildDir}/railsTests/config")
project.ext.rails.testPluginsDir = project.file("${project.buildDir}/railsTests/plugins")
project.ext.rails.testBundledPluginsDir = project.file("${project.rails.testPluginsDir}/bundled")
project.ext.rails.testExternalPluginsDir = project.file("${project.rails.testPluginsDir}/external")
project.ext.rails.testPluginsWorkDir = project.file("${project.rails.testPluginsDir}/work")

project.ext.railsSystemProperties = [
  'always.reload.config.file'                    : true,
  'cruise.i18n.cache.life'                       : 0,
  'cruise.config.dir'                            : project.rails.testConfigDir,
  'plugins.go.provided.path'                     : project.rails.testBundledPluginsDir,
  'plugins.external.provided.path'               : project.rails.testExternalPluginsDir,
  'plugins.work.path'                            : project.rails.testPluginsWorkDir,
  'rails.use.compressed.js'                      : false,
  'go.enforce.server.immutability'               : 'N',
  'jdk.net.URLClassPath.disableClassPathURLCheck': true
]

project.ext.railsTasksDefaultDependsOn = [
  ':server:initializeRailsGems',
  ':server:cleanDb',
  ':server:createJRubyBinstubs',
  project.sourceSets.main.runtimeClasspath,
  project.sourceSets.test.runtimeClasspath,
  project.configurations.spark,
  project.sourceSets.sharedTest.runtimeClasspath,
  project.sourceSets.fastUnitTest.runtimeClasspath
]

project.ext.jrubyexec = { Closure<JavaExecSpec> cl ->
  try {
    project.javaexec { JavaExecSpec javaExecSpec ->
      cl.delegate = javaExecSpec

      LinkedHashMap<String, Object> originalEnv = new LinkedHashMap<String, Object>(javaExecSpec.environment)

      ExecuteUnderRailsTask.setup(project, javaExecSpec, false)

      cl.call()

      ExecuteUnderRailsTask.debugEnvironment(javaExecSpec, originalEnv)
      ExecuteUnderRailsTask.dumpTaskCommand(javaExecSpec)
    }
  } finally {
    System.out.flush()
    System.err.flush()
  }
}

def props = [
  'go.enforce.server.immutability': 'N',
  'rails.use.compressed.js'       : 'false',
  'db.host'                       : 'localhost',
  'db.user'                       : 'sa',
  'db.password'                   : '',
  'db.name'                       : 'cruise',
  'db.port'                       : ''
]

props.putAll(project.hasProperty('testSystemProperties') ? project.testSystemProperties : [:])
project.ext.testSystemProperties = props

dependencies {
  jrubyGems project.deps.bundler
  compileOnly project.deps.jetBrainsAnnotations

  apiBase project(':api:api-base')
  allApis project(':api').subprojects
  spark project(':spark').subprojects
  implementation project(':addon-api:database')
  implementation project(':config:config-api')
  implementation project(':config:config-server')
  implementation project(':plugin-infra:go-plugin-access')
  implementation project(':common')
  implementation project(':rack_hack')
  implementation project(':db-support:db-migration')
  implementation project(':db-support:db-support-h2')
  implementation project(':db-support:db-support-postgresql')
  implementation project(':db-support:db-support-mysql')

  packagingOnly project(':base')
  packagingOnly project.deps.servletApi

  copyOnly project(path: ':tfs-impl:tfs-impl-14', configuration: 'fatJarConfig')
  copyOnly project(path: ':agent', configuration: 'fatJarConfig')
  copyOnly project(path: ':agent-launcher', configuration: 'fatJarConfig')
  copyOnlyTestData project(':test:test-agent')

  implementation project.deps.commonsDbcp
  api(project.deps.hibernate) {
    exclude(module: 'ehcache-core')
    exclude(module: 'commons-collections')
  }

  implementation project.deps.commonsFileUpload

  implementation project.deps.javaAssist
  implementation project.deps.mybatis
  implementation project.deps.mybatisSpring
  implementation project.deps.ehcache
  implementation(project.deps.oscache) {
    exclude(module: 'jms')
    exclude(module: 'servlet-api')
  }

  implementation project.deps.activeMQ

  implementation project.deps.jruby
  implementation project.deps.rack

  implementation project.deps.springOrm
  api project.deps.springWeb
  api project.deps.springContextSupport
  api(project.deps.springSecurityWeb) {
    exclude(group: 'org.springframework')
  }
  implementation(project.deps.springSecurity)
  implementation project.deps.aspectj
  implementation project.deps.aspectjWeaver
  implementation project.deps.urlrewrite
  implementation(project.deps.velocity) {
    exclude(module: 'commons-lang')
    exclude(module: 'commons-collections')
  }
  implementation files("vendor/velocity-tools-view-${project.versions.velocityToolsView}.jar")
  // old digester version required by velocity-tools-view
  implementation project.deps.commonsDigester

  // this is needed by velocity tools view
  implementation project.deps.commonsLang
  implementation project.deps.commonsCollections
  implementation project.deps.guava
  implementation project.deps.mail
  implementation project.deps.mailSmtp
  implementation project.deps.objenesis

  // needed by jdom2 for some XPATH stuff
  implementation(project.deps.jaxen) {
    exclude(module: 'xom')
    exclude(module: 'jdom')
  }
  implementation project.deps.slf4j
  implementation(project.deps.jgitServer) {
    exclude(module: 'jsch')
    exclude(module: 'jzlib')
  }
  compileOnly(project(':app-server')) {
    // need to exclude transitive deps or some required jars will be removed during packaging as we explicitly remove
    // these artifacts from packaging; this happens in the `war` task when excluding `doNotPackage` configurations
    transitive = false
  }
  annotationProcessor project.deps.lombok
  providedAtPackageTime(project.deps.logback) {
    exclude(module: 'slf4j-api') // Excluded to avoid transitive overriding the `implementation` dependency above
  }
  providedAtPackageTime project.deps.bouncyCastle
  providedAtPackageTime project.deps.bouncyCastlePkix
  providedAtPackageTime project.deps.jettyWebsocket

  testImplementation(project.deps.dbunit) {
    exclude(group: 'postgresql')
  }
  testImplementation project(path: ':common', configuration: 'testOutput')
  testImplementation project(path: ':config:config-server', configuration: 'testOutput')
  testImplementation project(path: ':config:config-api', configuration: 'testOutput')
  testImplementation project(path: ':test:test-utils')
  testImplementation project(':jetty9')
  testImplementation project.deps.jsonUnit
  testImplementation project.deps.jsoup
  testImplementation project.deps.xmlUnitMatchers
  testImplementation project.deps.assertJ
  testImplementation project.deps.xmlUnit

  testImplementation project(':test:http-mocks')

  testImplementation project.deps.httpClientMock

  testImplementation project.deps.junit5
  testImplementation project.deps.junit5Params
  testRuntimeOnly project.deps.junit5Engine
  testImplementation project.deps.mockitoJunit5
  integrationTestImplementation project.deps.springTestJunit5
}

jar {
  archiveClassifier = 'classes'
}

idea {
  module {
    testSourceDirs += sourceSets.sharedTest.java.srcDirs
    testSourceDirs += sourceSets.fastUnitTest.java.srcDirs
    testSourceDirs += sourceSets.integrationTest.java.srcDirs

    // some hax to fudge the intellij xml
    iml.withXml {
      def node = it.asNode()
      def content = node.component.find { it.'@name' == 'NewModuleRootManager' }.content[0]
      content.sourceFolder.each { sourceFolder ->
        if (sourceFolder.@url?.endsWith('/resources')) {
          sourceFolder.attributes().with {
            boolean isTestSource = (remove('isTestSource') == 'true')
            put('type', isTestSource ? 'java-test-resource' : 'java-resource')
          }
        }
      }
    }
  }
}

task sharedTest(type: Test) { thisTask ->
  description = 'Runs the functional tests.'
  group = 'verification'
  testClassesDirs = sourceSets.sharedTest.output.classesDirs
  classpath = sourceSets.sharedTest.runtimeClasspath
  mustRunAfter test

  reports {
    html.enabled = true
    junitXml.enabled = true
  }

  doFirst {
    def files = project.files([])
    project.sourceSets[thisTask.name].java.srcDirs.each { srcDir ->
      files = files.plus(project.fileTree(dir: srcDir, includes: ['**/*.java']))
    }

    def badFiles = files.findAll { eachFile ->
      def text = eachFile.getText("utf-8")
      text.contains("@Test")
    }

    if (!badFiles.isEmpty()) {
      throw new GradleException("${thisTask} is not supposed to contain any tests. The following files contained what looks like test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

task fastUnitTest(type: Test) { thisTask ->
  description = 'Runs the fast unit tests.'
  group = 'verification'

  testClassesDirs += sourceSets.fastUnitTest.output.classesDirs
  classpath += sourceSets.fastUnitTest.runtimeClasspath

  mustRunAfter test

  forkEvery 256
  minHeapSize '1g'
  maxHeapSize '4g'

  reports {
    html.enabled = true
    junitXml.enabled = true
  }

  doFirst {
    def files = project.files([])
    project.sourceSets[thisTask.name].java.srcDirs.each { srcDir ->
      files = files.plus(project.fileTree(dir: srcDir, includes: ['**/*.java']))
    }

    def badFiles = files.findAll { eachFile ->
      def text = eachFile.getText("utf-8")
      text.contains("SpringExtension") || text.contains("SpringJUnitJupiterConfig") || text.contains("@ContextConfiguration")
    }

    if (!badFiles.isEmpty()) {
      throw new GradleException("${thisTask} is not supposed to contain any integration tests. The following files contained what looks like integration test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

task integrationTest(type: Test) { thisTask ->
  description = 'Runs the integration tests.'
  group = 'verification'
  testClassesDirs += sourceSets.integrationTest.output.classesDirs
  classpath += sourceSets.integrationTest.runtimeClasspath
  mustRunAfter test

  forkEvery System.properties.containsKey('idea.home.path') ? 0 : 1
  minHeapSize '1024m'
  maxHeapSize '2048m'

  reports {
    html.enabled = true
    junitXml.enabled = true
  }

  doFirst {
    def excludes = [
      'com/thoughtworks/go/server/materials/MaterialDatabaseGitUpdaterTest.java',
      'com/thoughtworks/go/server/materials/MaterialDatabaseGitWithSubmodulesUpdaterTest.java',
      'com/thoughtworks/go/server/materials/MaterialDatabaseSvnWithExternalsUpdaterTest.java'
    ].collect { eachExclude -> FileSystems.getDefault().getPath(eachExclude) }

    def files = project.files([])
    project.sourceSets[thisTask.name].java.srcDirs.each { srcDir ->
      files = files.plus(project.fileTree(dir: srcDir, includes: ['**/*.java']))
    }

    def badFiles = files.findAll { eachFile ->
      def text = eachFile.getText("utf-8")
      text.contains("@Test") && !(text.contains("SpringExtension") || text.contains("SpringJUnitJupiterConfig") || text.contains("@ContextConfiguration"))
    }

    badFiles.removeIf { badFile ->
      excludes.any { eachExclude ->
        badFile.toPath().endsWith(eachExclude)
      }
    }

    if (!badFiles.isEmpty()) {
      throw new GradleException("${thisTask} is not supposed to contain any unit tests. The following files contained what looks like unit test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

task sharedTestJar(type: Jar) {
  description = "Assembles a jar archive containing the sharedTest classes."
  group = BasePlugin.BUILD_GROUP
  from sourceSets.sharedTest.output
  archiveClassifier = 'shared-test'
}

task fastUnitTestJar(type: Jar) {
  description = "Assembles a jar archive containing the fastUnitTest classes."
  group = BasePlugin.BUILD_GROUP
  from sourceSets.fastUnitTest.output
  archiveClassifier = 'fast-unit-test'
}

check.dependsOn(sharedTest, fastUnitTest, integrationTest)

task findGemsToNotPack {
  dependsOn ':server:initializeRailsGems'

  inputs.file("${project.railsRoot}/Gemfile")
  inputs.file("${project.railsRoot}/Gemfile.lock")
  inputs.file("find-gems-to-package.rake")
  ext.outputFile = file("${project.buildDir}/gems-to-package.json")
  outputs.file(outputFile)
  outputs.cacheIf { true }

  doFirst {
    project.jrubyexec {
      environment += [
        'OUTPUT_FILE'   : outputFile,
        'BUNDLE_GEMFILE': file("${project.railsRoot}/Gemfile")
      ]
      args = ['-S', 'rake', '--trace', '--rakefile', 'find-gems-to-package.rake']
    }
  }
}

task generateRubygemsLicenseReport {
  dependsOn ':server:initializeRailsGems'

  inputs.file("${project.railsRoot}/Gemfile")
  inputs.file("${project.railsRoot}/Gemfile.lock")
  inputs.file("rubygems-license-report.rake")
  ext.licenseReportFile = file("${project.buildDir}/rubygems-license.json")
  outputs.file(licenseReportFile)
  outputs.cacheIf { true }

  doFirst {
    project.jrubyexec {
      environment += [
        'OUTPUT_FILE'   : licenseReportFile,
        'BUNDLE_GEMFILE': file("${project.railsRoot}/Gemfile")
      ]
      args = ['-S', 'rake', '--trace', '--rakefile', 'rubygems-license-report.rake']
    }
  }
}

task cleanDb(type: Delete) {
  delete "db/h2db"
  delete "db/config.git"
}

clean.dependsOn cleanDb

task cleanTestRepo(type: Delete) {
  delete 'test-repo'
}

clean.dependsOn cleanTestRepo

task prepareTestGitRepo {
  File repoDir = project.file('test-repo')
  outputs.dir repoDir

  doFirst {
    project.delete(repoDir)
    repoDir.mkdirs()

    // `git init`
    Git git = Git.init().setDirectory(repoDir).call()

    git.getRepository().config.setBoolean("commit", null, "gpgsign", false)
    git.getRepository().config.save()

    // create a file
    project.file("${repoDir}/README.md").withPrintWriter { out ->
      out.println("# This is a test repo")
    }

    git.add().addFilepattern(".").call()
    git.commit()
      .setAuthor("GoCD Test User", "devnull@example.com")
      .setCommitter("GoCD Test User", "devnull@example.com")
      .setMessage("Initial commit").call()
  }
}

task copyTestDataJarsToServerDir(type: Copy) {
  from(project.configurations.copyOnlyTestData)
  into 'testdata'
  rename "(.*)-${project.version}.jar", '$1.jar'
}

task copyAgentJarsToServerDir(type: Copy) {
  from(project.configurations.copyOnly)
  into '.'
  rename "(.*)-${project.version}.jar", '$1.jar'
}

task copyAgentSideJarsToServerDir(dependsOn: [copyAgentJarsToServerDir, copyTestDataJarsToServerDir])

task prepareNoRails {
  dependsOn = [cleanDb, ':plugin-infra:go-plugin-activator:jar', copyAgentJarsToServerDir]
}

task prepare {
  dependsOn = [prepareNoRails, prepareTestGitRepo, ':server:compileAssetsDev']
}

test {
  dependsOn prepareNoRails
}

test { Test thisTask ->
  // because the rails codebase is part of the test sourcetree
  dependsOn licenseTest
  dependsOn licenseMain
  doFirst {
    def files = project.files([])
    project.sourceSets[thisTask.name].java.srcDirs.each { srcDir ->
      files = files.plus(project.fileTree(dir: srcDir, includes: ['**/*.java']))
    }

    def badFiles = files.findAll { eachFile ->
      def text = eachFile.getText("utf-8")
      text.contains("@Test")
    }

    if (!badFiles.isEmpty()) {
      throw new GradleException("${thisTask} is not supposed to contain any unit tests. Move the tests to fast-test or integration-test as appropriate. The following files contained what looks like test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

tasks.withType(Test) {
  dependsOn copyAgentSideJarsToServerDir
  dependsOn prepareNoRails

  def heapDumpDir = project.file("${buildDir}/heap-dumps")
  if (project.hasProperty("heapDumpOnOOM")) {
    jvmArgs("-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=${heapDumpDir}")
  }

  doFirst {
    project.delete(heapDumpDir)
    project.mkdir(heapDumpDir)
    project.file("${heapDumpDir}/ignore.txt").createNewFile()
  }

  doLast {
    project.fileTree(heapDumpDir).files.each { File eachFile ->
      ant.gzip(src: eachFile, destfile: "${eachFile}.gz")
      project.delete eachFile
    }
  }

  systemProperties += project.testSystemProperties

  classpath += project.files(project.configurations.additionalClasspath.files)

  testLogging {
    showStandardStreams = false
    exceptionFormat 'full'
  }

  onOutput { TestDescriptor descriptor, TestOutputEvent event ->
    if (event.message.startsWith("Compiler warnings:")) {
      return
    }

    if (event.message.startsWith("  WARNING:  'org.apache.xerces.jaxp.SAXParserImpl:")) {
      return
    }

    if (event.message.startsWith("Warning:  org.apache.xerces.parsers.SAXParser:")) {
      return
    }

    if (event.message.startsWith("Warning:  org.apache.xerces.jaxp.SAXParserImpl\$JAXPSAXParser")) {
      return
    }

    if (event.destination == TestOutputEvent.Destination.StdOut) {
      logger.quiet(TextUtil.toPlatformLineSeparators(TextUtil.indent(event.getMessage(), "  OUT: ")))
    } else if (event.destination == TestOutputEvent.Destination.StdErr) {
      logger.quiet(TextUtil.toPlatformLineSeparators(TextUtil.indent(event.getMessage(), "  ERR: ")))
    }
  }
}

clean {
  delete 'artifacts'
  delete 'artifactsDir'
  delete 'data'
  delete 'felix-cache'
  delete 'pipelines'
  delete 'plugins'
  delete 'tfs-impl-14.jar'
  delete 'agent-launcher.jar'
  delete 'agent.jar'
  delete 'plugins_work'
  delete 'testdata/test-agent.jar'
  delete 'config/agentkeystore'
  delete 'config/keystore'
  delete 'config/truststore'
}

task configureWar {
  dependsOn findGemsToNotPack
  doFirst {
    war.from(project.webAppDir) { theSpec ->
      into '/'
      exclude('WEB-INF/web.xml')
      exclude("**/rails/*.log")
      exclude("**/rails/log/*")
      exclude("**/rails/node_modules/")
      exclude("**/rails/spec/")
      exclude("**/rails/tmp/")
      exclude("**/rails/**/jruby/*/cache/*.gem")
      exclude("**/rails/app/assets/")
      exclude("**/rails/webpack/")
    new JsonSlurper().parse(findGemsToNotPack.outputFile).each { gem ->
        theSpec.exclude("**/rails/gems/jruby/${RUBY_VERSION}/gems/${gem}")
        theSpec.exclude("**/rails/gems/jruby/${RUBY_VERSION}/specifications/${gem}.gemspec")
        theSpec.exclude("**/rails/gems/jruby/${RUBY_VERSION}/build_info/${gem}.info")
      }
    }
  }
}

task war(type: Jar) {
  dependsOn rootProject.generateLicenseReport
  dependsOn configureWar
  dependsOn ':server:jar', ':server:compileAssets'
  dependsOn project.configurations.compileClasspath
  dependsOn project.configurations.allApis
  dependsOn project.configurations.spark
  finalizedBy 'verifyWar'
  archiveBaseName = 'cruise'
  archiveExtension = 'war'

  // Using a Set<String> is more reliable than Set<File> because File.equals() may be affected by other internal fields
  // aside from `path` that aren't important to us; thus, Set<File> may allow duplicate paths and Set<String> will not.
  Set<String> classpath = (
    /*
     * Order of operations when combining and subtracting lists matters in Groovy in non-obvious ways (* see below),
     * so the parentheses are here to make this more resilient to refactoring as well as communicating intent.
     *
     * Order matters in Groovy - here's how:
     *   [1, 2] + [5] - [5] + [5] == [1, 2, 5] // so far, so good
     *   [1, 2] + [5] + [5] - [5] == [1, 2]    // surprise! subtracting [5] removes all instances of 5
     */
      project.configurations.packagingOnly + (
      project.configurations.allApis +
      project.configurations.spark +
      project.configurations.compileClasspath -
      project.configurations.doNotPackage
    )
  ).collect {f -> f.toString()}.toSet()

  // exclude app-server because it causes class cast exceptions (loaded by different classloaders)
  classpath.removeAll([project(':app-server').jar.archiveFile.get().toString(), jar.archiveFile.get().toString()])

  from(classpath) {
    into 'WEB-INF/lib'
  }

  from(zipTree(jar.archiveFile)) {
    into('WEB-INF/classes')
  }

  from("${project.webAppDir}/WEB-INF/rails/webpack/rails-shared/plugin-endpoint.js") {
    into('WEB-INF/classes')
  }

  from("${project(':config:config-server').sourceSets.main.resources}/cruise-config.xsd") {
    into '/'
  }

  from(project.project(':config:config-server').sourceSets.main.resources.srcDirs) {
    into('/') include('cruise-config.xsd')
  }
  from("messages/message.properties") { into('/') }

  from({ compileAssets }) { into '/WEB-INF/rails/public/assets' }

  from("${project.webAppDir}/WEB-INF/web.xml") {
    filter {
      it.replaceAll('<param-value>development</param-value>', '<param-value>production</param-value>')
    }
    into "/WEB-INF"
  }
}

task verifyWar(type: VerifyJarTask) {
  jarTask = war
  expectedJars = [
    "WEB-INF/lib": (
      ((project(':api').subprojects + project(':spark').subprojects).collect { eachProject -> eachProject.jar.archiveFile.get().asFile.name })
        + [
        "FastInfoset-1.2.15.jar",
        "JavaEWAH-1.1.12.jar",
        "activation-1.1.jar",
        "activemq-broker-${project.versions.activeMQ}.jar",
        "activemq-client-${project.versions.activeMQ}.jar",
        "activemq-openwire-legacy-${project.versions.activeMQ}.jar",
        "ant-${project.versions.apacheAnt}.jar",
        "antlr-2.7.6.jar",
        "aopalliance-1.0.jar",
        "asm-7.1.jar",
        "aspectjrt-${project.versions.aspectj}.jar",
        "aspectjweaver-${project.versions.aspectj}.jar",
        "base-${project.version}.jar",
        "cglib-${project.versions.cglib}.jar",
        "checker-qual-3.12.0.jar",
        "cloning-${project.versions.cloning}.jar",
        "commandline-${project.version}.jar",
        "common-${project.version}.jar",
        "commons-beanutils-${project.versions.commonsBeanutils}.jar",
        "commons-codec-${project.versions.commonsCodec}.jar",
        "commons-collections-${project.versions.commonsCollections}.jar",
        "commons-collections4-${project.versions.commonsCollections4}.jar",
        "commons-dbcp2-${project.versions.commonsDbcp}.jar",
        "commons-digester-${project.versions.commonsDigester}.jar",
        "commons-fileupload-${project.versions.commonsFileUpload}.jar",
        "commons-io-${project.versions.commonsIO}.jar",
        "commons-lang-${project.versions.commonsLang}.jar",
        "commons-lang3-${project.versions.commonsLang3}.jar",
        "commons-pool2-${project.versions.commonsPool}.jar",
        "config-api-${project.version}.jar",
        "config-server-${project.version}.jar",
        "db-${project.version}.jar",
        "db-migration-${project.version}.jar",
        "db-support-base-${project.version}.jar",
        "db-support-h2-${project.version}.jar",
        "db-support-mysql-${project.version}.jar",
        "db-support-postgresql-${project.version}.jar",
        "dom4j-${project.versions.dom4j}.jar",
        "domain-${project.version}.jar",
        "ehcache-${project.versions.ehcache}.jar",
        "error_prone_annotations-2.7.1.jar",
        "failureaccess-1.0.1.jar",
        "freemarker-${project.versions.freemarker}.jar",
        "geronimo-j2ee-management_1.1_spec-1.0.1.jar",
        "geronimo-jms_1.1_spec-1.1.1.jar",
        "go-plugin-access-${project.version}.jar",
        "go-plugin-activator-${project.version}.jar",
        "go-plugin-api-${project.version}.jar",
        "go-plugin-api-internal-${project.version}.jar",
        "go-plugin-config-repo-${project.version}.jar",
        "go-plugin-domain-${project.version}.jar",
        "go-plugin-infra-${project.version}.jar",
        "gson-${project.versions.gson}.jar",
        "guava-${project.versions.guava}.jar",
        "h2-${project.versions.h2}.jar",
        "hawtbuf-1.11.jar",
        "hibernate-commons-annotations-3.2.0.Final.jar",
        "hibernate-core-${project.versions.hibernate}.jar",
        "hibernate-ehcache-${project.versions.hibernate}.jar",
        "hibernate-jpa-2.0-api-1.0.1.Final.jar",
        "httpclient-${project.versions.apacheHttpComponents}.jar",
        "httpcore-4.4.13.jar",
        "httpmime-${project.versions.apacheHttpComponents}.jar",
        "istack-commons-runtime-3.0.7.jar",
        "j2objc-annotations-1.3.jar",
        "jackson-annotations-${project.versions.jackson}.jar",
        "jackson-core-${project.versions.jackson}.jar",
        "jackson-databind-${project.versions.jackson}.jar",
        "javassist-${project.versions.javaAssist}.jar",
        "javax.activation-api-1.2.0.jar",
        "javax.annotation-api-${project.versions.javaxAnnotation}.jar",
        "javax.inject-1.jar",
        "javax.servlet-api-${project.versions.servletApi}.jar",
        "jaxb-api-${project.versions.jaxb}.jar",
        "jaxb-runtime-${project.versions.jaxb}.jar",
        "jaxen-${project.versions.jaxen}.jar",
        "jcl-over-slf4j-${project.versions.slf4j}.jar",
        "jdom2-${project.versions.jdom}.jar",
        "joda-time-${project.versions.jodaTime}.jar",
        "jolt-core-${project.versions.jolt}.jar",
        "jruby-complete-${project.versions.jruby}.jar",
        "jruby-rack-${project.versions.rack}.jar",
        "json-utils-${project.versions.jolt}.jar",
        "jsr305-3.0.2.jar",
        "jta-1.1.jar",
        "liquibase-core-${project.versions.liquibase}.jar",
        "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
        "mailapi-${project.versions.mail}.jar",
        "mybatis-${project.versions.mybatis}.jar",
        "mybatis-spring-${project.versions.mybatisSpring}.jar",
        "mysql-connector-java-${project.versions.mysql}.jar",
        "objenesis-${project.versions.objenesis}.jar",
        "org.apache.felix.framework-${project.versions.felix}.jar",
        "org.eclipse.jgit-${project.versions.jgit}.jar",
        "org.eclipse.jgit.http.server-${project.versions.jgit}.jar",
        "oscache-${project.versions.oscache}.jar",
        "plugin-metadata-store-${project.version}.jar",
        "postgresql-${project.versions.postgresql}.jar",
        "protobuf-java-3.11.4.jar",
        "quartz-${project.versions.quartz}.jar",
        "rack_hack-${project.version}.jar",
        "semantic-version-${project.versions.semanticVersion}.jar",
        "slf4j-api-${project.versions.slf4j}.jar",
        "smtp-${project.versions.mail}.jar",
        "spark-core-${project.versions.spark}.jar",
        "spring-aop-${project.versions.spring}.jar",
        "spring-beans-${project.versions.spring}.jar",
        "spring-context-${project.versions.spring}.jar",
        "spring-context-support-${project.versions.spring}.jar",
        "spring-core-${project.versions.spring}.jar",
        "spring-expression-${project.versions.spring}.jar",
        "spring-jdbc-${project.versions.spring}.jar",
        "spring-orm-${project.versions.spring}.jar",
        "spring-security-config-${project.versions.springSecurity}.jar",
        "spring-security-core-${project.versions.springSecurity}.jar",
        "spring-security-web-${project.versions.springSecurity}.jar",
        "spring-tx-${project.versions.spring}.jar",
        "spring-web-${project.versions.spring}.jar",
        "spring-webmvc-${project.versions.spring}.jar",
        "stax-ex-1.8.jar",
        "token-bucket-${project.versions.tokenBucket}.jar",
        "txw2-${project.versions.jaxb}.jar",
        "urlrewritefilter-${project.versions.urlrewrite}.jar",
        "util-${project.version}.jar",
        "velocity-${project.versions.velocity}.jar",
        "velocity-tools-view-${project.versions.velocityToolsView}.jar",
        "zt-exec-${project.versions.ztExec}.jar",
      ])
  ]
}

task licenseReportAggregate {
  if (!project.hasProperty('fastBuild')) {
    rootProject.allprojects.each { eachProject ->
      def reportTasks = eachProject.tasks.withType(ReportTask)
      if (!reportTasks.isEmpty()) {
        licenseReportAggregate.dependsOn reportTasks
        licenseReportAggregate.inputs.dir(eachProject.licenseReport.outputDir)
      }
    }
    dependsOn generateLicenseReport
  }

  onlyIf { !project.hasProperty('fastBuild') }

  def licenseReportAggregateOutputDir = project.file("${project.buildDir}/reports/license-report-aggregate")
  outputs.dir(licenseReportAggregateOutputDir)

  LinkedHashSet<Map<String, Object>> missingLicenseData = [
    [
      moduleName    : 'antlr:antlr',
      moduleVersion : '2.7.6',
      moduleUrls    : [
        "https://www.antlr.org"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'BSD-3-Clause',
          moduleLicenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html"
        ]
      ]
    ],
    [
      moduleName    : 'org.ow2.asm:asm',
      moduleVersion : '7.1',
      moduleUrls    : [
        "https://asm.ow2.io/"
      ],
      moduleLicenses: [
        [
          // License is automatically detected as 0BSD by gradle-license-report, but appears to actually be BSD-3-Clause in nature
          moduleLicense   : 'BSD-3-Clause',
          moduleLicenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html"
        ]
      ]
    ],
    [
      moduleName    : 'dom4j:dom4j',
      moduleVersion : '1.6.1',
      moduleUrls    : [
        "https://dom4j.github.io/"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'dom4j BSD license',
          moduleLicenseUrl: "https://github.com/dom4j/dom4j/blob/dom4j_1_6_1/LICENSE.txt"
        ]
      ]
    ],
    [
      moduleName    : 'com.bazaarvoice.jolt:jolt-core',
      moduleVersion : project.versions.jolt,
      moduleUrls    : [
        "https://github.com/bazaarvoice/jolt"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : 'com.bazaarvoice.jolt:json-utils',
      moduleVersion : project.versions.jolt,
      moduleUrls    : [
        "https://github.com/bazaarvoice/jolt"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : ':com.microsoft.tfs.sdk',
      moduleVersion : '14.118.0',
      moduleUrls    : [
        "https://github.com/Microsoft/team-explorer-everywhere"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'MIT',
          moduleLicenseUrl: "https://spdx.org/licenses/MIT.html"
        ]
      ]
    ],
    [
      moduleName    : 'velocity-tools:velocity-tools-view',
      moduleVersion : project.versions.velocityToolsView,
      moduleUrls    : [
        "https://velocity.apache.org"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : 'org.hibernate.javax.persistence:hibernate-jpa-2.0-api',
      moduleVersion : '1.0.1.Final',
      moduleUrls    : [
        "http://hibernate.org"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Eclipse Distribution License - v1.0',
          moduleLicenseUrl: "http://www.eclipse.org/org/documents/edl-v10.php"
        ]
      ]
    ],
    [
      moduleName    : 'jaxen:jaxen',
      moduleVersion : project.versions.jaxen,
      moduleUrls    : [
        "http://www.cafeconleche.org/jaxen/"
      ],
      moduleLicenses: [
        [
          // License is automatically detected as 0BSD by gradle-license-report, but appears to actually be BSD-3-Clause in nature
          moduleLicense   : 'BSD-3-Clause',
          moduleLicenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html"
        ]
      ]
    ],
  ]

  def goCDJarsWithoutLicenses = ['agent-launcher.jar',
                                 'agent.jar',
                                 'go-plugin-activator.jar',
                                 'tfs-impl-14.jar',
                                 'gocd-json-config-plugin.jar',
                                 'gocd-yaml-config-plugin.jar',
                                 'gocd-filebased-authentication-plugin.jar',
                                 'gocd-ldap-authentication-plugin.jar',
                                 'gocd-yum-repository-poller-plugin.jar',
                                 'gocd-file-based-secrets-plugin.jar']

  def getAllJarDependencies = {
    // get a list of all dependencies from each gradle module
    def allDependenciesInReport = rootProject.allprojects.inject(new LinkedHashSet<Map<String, Object>>(), { result, eachProject ->
      def file = eachProject.file("${eachProject.licenseReport.outputDir}/index.json")
      if (file.exists()) {
        Map<String, Object> json = new JsonSlurper().parse(file)
        if (json.dependencies != null) {
          result.addAll(json.dependencies)
        }
      }
      result
    })

    // remove items that we know to have missing license data
    allDependenciesInReport.removeIf { eachDependency ->
      !!missingLicenseData.find { dependencyWithMissingLicenseData ->
        eachDependency.moduleName == dependencyWithMissingLicenseData.moduleName && eachDependency.moduleVersion == dependencyWithMissingLicenseData.moduleVersion
      }
    }

    // then add the missing license data
    allDependenciesInReport.addAll(missingLicenseData)

    allDependenciesInReport
  }

  def getLicenseDataFor = { Set<String> allJars, allDependenciesInReport ->
    allJars.inject([:], { Map<String, Map<String, Object>> result, String eachPackagedJar ->
      Map<String, Object> module = allDependenciesInReport.find { Map<String, Object> eachDependencyInReport ->
        def (groupId, artifactId) = eachDependencyInReport.moduleName.split(':')
        def version = eachDependencyInReport.moduleVersion
        eachPackagedJar == "${artifactId}-${version}.jar"
      }

      if (module == null) {
        throw new GradleException("Could not find license details for packaged jar ${eachPackagedJar}")
      }

      result[module.moduleName] = module
      result
    })
  }

  doFirst {
    LinkedHashSet<Map<String, Object>> allDependenciesInReport = getAllJarDependencies()

    List<VerifyJarTask> verifyJarTasks = rootProject.allprojects.collect { eachProject ->
      eachProject.tasks.withType(VerifyJarTask)
    }.flatten()

    // list of all jars that we package
    Set<String> allJars = verifyJarTasks.collect { VerifyJarTask eachTask ->
      eachTask.expectedJars.values().flatten()
    }.flatten().collect { name -> name.toString() }.sort().unique()

    // remove unwanted stuff, mostly nested zip files and jars with GoCD package version
    allJars.removeIf { name -> name.endsWith(".zip") || name.endsWith("-${project.version}.jar") || name.contains("-${project.version}-") }
    allJars.removeAll(goCDJarsWithoutLicenses)

    // get the licenses corresponding to the jars we actually package.
    Map<String, Map<String, Object>> licensesForPackagedJarDependencies = getLicenseDataFor(allJars, allDependenciesInReport)

    project.delete(licenseReportAggregateOutputDir)
    project.mkdir(licenseReportAggregateOutputDir)

    def licenseReport = new LicenseReport(project, licenseReportAggregateOutputDir, licensesForPackagedJarDependencies, compileAssetsWebpackProd.ext.licenseReportFile, generateRubygemsLicenseReport.ext.licenseReportFile)
    licenseReport.generate()
  }
}

apply from: 'rails.gradle'
apply from: 'jasmine.gradle'
apply from: 'helpers.gradle'
apply from: 'lint.gradle'

generateLicenseReport.dependsOn(compileAssetsWebpackProd, generateRubygemsLicenseReport)

task verifyJar {
  dependsOn verifyWar
}

artifacts {
  warConfig war
  jarConfig jar

  sharedTestOutput sharedTestJar
}

Output:
【漏洞类型】XSS（跨站脚本攻击）
【严重性】High
【漏洞摘要】此代码存在跨站脚本攻击漏洞。具体表现为在`DirectoryEntries.java`和`FolderDirectoryEntry.java`文件中，方法`render`和`htmlBody`中直接使用用户输入的文件名和URL（如`getFileName()`和`getUrl()`）作为HTML内容，而没有进行适当的HTML转义。这会导致攻击者通过恶意文件名或URL注入JavaScript代码，从而在用户的浏览器中执行恶意脚本。
【关键代码模式】
- `HtmlElement.content(String body)`
- `HtmlElement.content(HtmlRenderable... elements)`
- `HtmlElement.a(HtmlAttribute.href(getUrl())).content(getFileName())`

【修复方法说明】修复方法通过引入`commons-text`库，并在`HtmlElement`类中添加`safecontent`方法，对用户输入的内容进行HTML转义处理，从而防止XSS攻击。