diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 81afd7b7a..ac302b605 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -22,6 +22,14 @@ jobs: with: distribution: 'temurin' java-version: '17' + cache: maven + server-id: pixee + server-username: ${{ vars.PIXEE_ARTIFACTORY_USERNAME }} + server-password: ${{ secrets.PIXEE_ARTIFACTORY_PASSWORD }} + - name: Set up Maven + uses: stCarolas/setup-maven@v4.4 + with: + maven-version: 3.8.2 - uses: gradle/gradle-build-action@v2 with: arguments: check --stacktrace diff --git a/gradle/build-plugins/build.gradle.kts b/gradle/build-plugins/build.gradle.kts index d9fd4b11c..c78f390fb 100644 --- a/gradle/build-plugins/build.gradle.kts +++ b/gradle/build-plugins/build.gradle.kts @@ -24,4 +24,5 @@ dependencies { implementation(buildlibs.spotless) implementation(buildlibs.nebula.publish.plugin) implementation(buildlibs.nebula.contacts.plugin) + implementation(buildlibs.kotlin) } diff --git a/gradle/build-plugins/src/main/kotlin/io.codemodder.kotlin.gradle.kts b/gradle/build-plugins/src/main/kotlin/io.codemodder.kotlin.gradle.kts new file mode 100644 index 000000000..f05d74fee --- /dev/null +++ b/gradle/build-plugins/src/main/kotlin/io.codemodder.kotlin.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("io.codemodder.spotless") + id("org.jetbrains.kotlin.jvm") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +spotless { + java { + googleJavaFormat() + } +} + +tasks.withType(Test::class) { + useJUnitPlatform() +} diff --git a/gradle/buildlibs.versions.toml b/gradle/buildlibs.versions.toml index f9385bc61..d73324bf5 100644 --- a/gradle/buildlibs.versions.toml +++ b/gradle/buildlibs.versions.toml @@ -9,6 +9,7 @@ nebula-contacts-plugin = { module = "com.netflix.nebula.contacts:com.netflix.neb nebula-mavenscm-plugin = { module = "com.netflix.nebula.maven-scm:com.netflix.nebula.maven-scm.gradle.plugin", version = "20.3.0"} nebula-publish-plugin = { module = "com.netflix.nebula.maven-publish:com.netflix.nebula.maven-publish.gradle.plugin", version = "20.3.0"} spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } +kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin", version = "1.8.20"} [plugins] fileversioning = { id = "de.epitschke.gradle-file-versioning", version.ref = "fileversioning" } diff --git a/pom-operator/.github/badges/branches.svg b/pom-operator/.github/badges/branches.svg new file mode 100644 index 000000000..1d4e5797d --- /dev/null +++ b/pom-operator/.github/badges/branches.svg @@ -0,0 +1 @@ +branches74% \ No newline at end of file diff --git a/pom-operator/.github/badges/jacoco.svg b/pom-operator/.github/badges/jacoco.svg new file mode 100644 index 000000000..d17718247 --- /dev/null +++ b/pom-operator/.github/badges/jacoco.svg @@ -0,0 +1 @@ +coverage90.5% \ No newline at end of file diff --git a/pom-operator/.github/pull_request_template.md b/pom-operator/.github/pull_request_template.md new file mode 100644 index 000000000..647353c41 --- /dev/null +++ b/pom-operator/.github/pull_request_template.md @@ -0,0 +1,15 @@ +# Description + +# Checklist: + +- [ ] I did my own review +- [ ] Commented Code - particularly in hard sections +- [ ] Docs (Javadocs) +- [ ] No TODOs / Issues on Code +- [ ] Tests: Unit Testing +- [ ] Tests: Assertions Look Good and Sane +- [ ] Tests: Coverage +- [ ] Integration Testing +- [ ] Java Test Fixtures +- [ ] Security Risks Considered and Addressed / Mitigated + \ No newline at end of file diff --git a/pom-operator/.github/workflows/push-test.yml b/pom-operator/.github/workflows/push-test.yml new file mode 100644 index 000000000..2504f91ef --- /dev/null +++ b/pom-operator/.github/workflows/push-test.yml @@ -0,0 +1,81 @@ +name: Java CI + +on: + push: + branches-ignore: + - 'master' + +env: + MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2" + +jobs: + build: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.platform }} + environment: + name: default + env: + ARTIFACTORY_USERNAME_REF: "${{ secrets.ARTIFACTORY_USERNAME_REF }}" + ARTIFACTORY_TOKEN_REF: "${{ secrets.ARTIFACTORY_TOKEN_REF }}" + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + server-id: pixee + server-username: ARTIFACTORY_USERNAME_REF + server-password: ARTIFACTORY_TOKEN_REF + - name: Set up Maven + uses: stCarolas/setup-maven@v4.4 + with: + maven-version: 3.8.2 + - name: Resolve all Dependencies + run: | + mvn -N -B dependency:go-offline + - name: Build with Maven + run: | + mvn -N -B install + - name: Integration Testing (Linux Only) + if: matrix.platform == 'ubuntu-latest' + run: | + mvn -N -B -Dmaven.test.skip install + mvn -N -B -P integration-testing verify + - name: Generate Coverage Badge + id: jacoco + uses: cicirello/jacoco-badge-generator@v2 + if: ${{ matrix.platform == 'ubuntu-latest' }} + with: + generate-branches-badge: true + - name: Log coverage percentage + if: ${{ matrix.platform == 'ubuntu-latest' }} + run: | + echo "coverage = ${{ steps.jacoco.outputs.coverage }}" + echo "branch coverage = ${{ steps.jacoco.outputs.branches }}" + - # When using "Act", disable certain actions, starting with this one + # See https://github.com/nektos/act#skipping-steps for more details + name: Commit the JaCoCo badge (if it changed) + if: ${{ matrix.platform == 'ubuntu-latest' && !env.ACT }} + run: | + if [[ `git status --porcelain` ]]; then + git config --global user.name 'Jacoco Coverage Update Action' + git config --global user.email 'pixee@users.noreply.github.com' + git add -A + git commit -m "[no ci] Autogenerated JaCoCo coverage badge" + git push + fi + - name: Upload JaCoCo coverage report + if: ${{ matrix.platform == 'ubuntu-latest' && !env.ACT }} + uses: actions/upload-artifact@v2 + with: + name: jacoco-report + path: target/site/jacoco/ + + - name: Deploy (only if on master) + if: ${{ github.ref_name == 'master' && matrix.platform == 'ubuntu-latest' && !env.ACT }} + run: | + mvn -N -B deploy -DaltDeploymentRepository=pixee::default::https://pixee.jfrog.io/artifactory/mailman-libs-snapshot diff --git a/pom-operator/.github/workflows/release.yml b/pom-operator/.github/workflows/release.yml new file mode 100644 index 000000000..ff8116272 --- /dev/null +++ b/pom-operator/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Create Release on Semantic Version Tag + +on: + push: + tags: + - "v*" + +env: + M2_HOME: /opt/hostedtoolcache/maven/3.8.2 + JAVA_HOME: /opt/hostedtoolcache/Java_Adopt_jdk/11.0.17-8 + +jobs: + release: + runs-on: "ubuntu-latest" + + steps: + - uses: actions/checkout@v3 + + - id: setup-java + name: "Build, test and deploy" + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Set up Maven + uses: stCarolas/setup-maven@v4.4 + with: + maven-version: 3.8.2 + + - name: "Publish to Maven Central" + run: | + mvn -N -B \ + -Prelease \ + -X \ + --no-transfer-progress \ + --batch-mode \ + clean \ + deploy + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSPHRASE }} diff --git a/pom-operator/.gitignore b/pom-operator/.gitignore new file mode 100644 index 000000000..b8115cd81 --- /dev/null +++ b/pom-operator/.gitignore @@ -0,0 +1,69 @@ +### Maven template +.cache/ + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/ + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +.secrets diff --git a/pom-operator/README.md b/pom-operator/README.md new file mode 100644 index 000000000..ff4274fda --- /dev/null +++ b/pom-operator/README.md @@ -0,0 +1,68 @@ +[![Actions Status](https://github.com/pixee/pom-operator/workflows/Java%20CI/badge.svg)](https://github.com/pixee/pom-operator/actions) +![Coverage](.github/badges/jacoco.svg) + +# pom-operator + +POM Operator is a library responsible for injecting dependencies into POM files programatically. + +## Building + +Use [Maven](https://maven.apache.org): + +``` +$ git clone git@github.com:pixee/pom-operator.git && cd pom-operator +$ mvn clean install +``` + +## Using + +There's a sample of usage from Java on the `java-sample` directory - of look under the `src/test` directory as well. TL;DR: + +```java +import org.junit.Test; + +import io.github.pixee.maven.operator.ProjectModel; +import io.github.pixee.maven.operator.Dependency; +import io.github.pixee.maven.operator.POMOperator; +import io.github.pixee.maven.operator.ProjectModelFactory; + +public class POMOperatorJavaTest { + @Test + public void testInterop() { + ProjectModel projectModel = ProjectModelFactory + .load(POMOperatorJavaTest.class.getResource("pom.xml")) + .withDependency( + new Dependency("org.dom4j", "dom4j", "0.0.0", null, "jar") + ); + + POMOperator.modify(projectModel); + } +} + +``` + +## How it works? + +It implements a Chain of Responsibility strategy - each `Command` class attempts a different way of fixing a POM, based around a Context (in this case, a `ProjectModel`) + +## Releasing + +e.g. to generate version `0.0.2`: + +``` +(mvn versions:set -DnewVersion=0.0.3 && git commit -am "Generating Tag" && git tag v0.0.3 && git push && git push --tags) +(export V='0.0.11-SNAPSHOT' ; mvn versions:set -DnewVersion=${V} && (cd java-sample ; mvn versions:set -DnewVersion=${V} && git commit -am "Generating development version" && git push)) +``` + +# TODO: + +Deploying: + +``` +mvn -N -B deploy -DaltDeploymentRepository=pixee-libs-release::default::https://pixee.jfrog.io/artifactory/default-maven-local +``` + +- ~~better readme~~ +- ~~be able to guess existing indenting for existing documents~~ +- ~~investigate leverage whats on [versions-maven-plugin](https://github.com/mojohaus/versions-maven-plugin)~~ +- consider fuzzying when testing diff --git a/pom-operator/build.gradle.kts b/pom-operator/build.gradle.kts new file mode 100644 index 000000000..f7f78420d --- /dev/null +++ b/pom-operator/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("io.codemodder.kotlin") +} + +dependencies { + api("com.offbytwo:docopt:0.6.0.20150202") + api("org.apache.commons:commons-lang3:3.12.0") + api("org.dom4j:dom4j:2.1.3") + api("jaxen:jaxen:1.2.0") + api("xerces:xercesImpl:2.12.2") + api("org.xmlunit:xmlunit-core:2.9.0") + api("org.xmlunit:xmlunit-assertj3:2.9.0") + api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10") + api("com.github.zafarkhaja:java-semver:0.9.0") + api("commons-io:commons-io:2.11.0") + api("org.apache.maven.shared:maven-invoker:3.2.0") + api("com.google.inject:guice:5.1.0") + api("org.apache.maven:maven-embedder:3.8.6") { + exclude(group = "com.google.inject", module = "guice") + } + api("org.apache.maven:maven-compat:3.8.6") { + exclude(group = "com.google.inject", module = "guice") + } + api("org.apache.maven.resolver:maven-resolver-api:1.9.2") + api("org.apache.maven.resolver:maven-resolver-spi:1.9.2") + api("org.apache.maven.resolver:maven-resolver-util:1.9.2") + api("org.apache.maven.resolver:maven-resolver-impl:1.9.2") + api("org.apache.maven.resolver:maven-resolver-transport-file:1.9.2") + api("org.apache.maven.resolver:maven-resolver-transport-http:1.9.2") + api("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.2") + api("org.apache.maven:maven-resolver-provider:3.8.6") + api("org.apache.maven:maven-model-builder:3.8.6") + api("com.github.albfernandez:juniversalchardet:2.4.0") + testImplementation("org.slf4j:slf4j-simple:2.0.0") + testImplementation("fun.mike:diff-match-patch:0.0.2") + testImplementation("io.github.java-diff-utils:java-diff-utils:4.12") + testImplementation("org.hamcrest:hamcrest-all:1.3") + testImplementation(testlibs.bundles.junit.jupiter) + compileOnly("org.slf4j:slf4j-api:2.0.0") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} diff --git a/pom-operator/java-sample/pom.xml b/pom-operator/java-sample/pom.xml new file mode 100644 index 000000000..4e1578f04 --- /dev/null +++ b/pom-operator/java-sample/pom.xml @@ -0,0 +1,88 @@ + + + + 4.0.0 + + io.github.pixee.maven.sample + java-sample + 0.0.22-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + + + + + io.github.pixee.maven + pom-operator + ${project.version} + + + org.slf4j + slf4j-api + 2.0.0 + + + junit + junit + 4.13.2 + test + + + org.apache.maven + maven-embedder + 3.8.6 + provided + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/pom-operator/java-sample/src/test/java/io/github/pixee/it/POMOperatorJavaTest.java b/pom-operator/java-sample/src/test/java/io/github/pixee/it/POMOperatorJavaTest.java new file mode 100644 index 000000000..d6267f3b4 --- /dev/null +++ b/pom-operator/java-sample/src/test/java/io/github/pixee/it/POMOperatorJavaTest.java @@ -0,0 +1,19 @@ +package io.github.pixee.it; + +import org.junit.Test; + +import io.github.pixee.maven.operator.ProjectModel; +import io.github.pixee.maven.operator.Dependency; +import io.github.pixee.maven.operator.POMOperator; +import io.github.pixee.maven.operator.ProjectModelFactory; + +public class POMOperatorJavaTest { + @Test + public void testInterop() { + ProjectModel projectModel = ProjectModelFactory.load(POMOperatorJavaTest.class.getResource("pom.xml")) + .withDependency(new Dependency("org.dom4j", "dom4j", "0.0.0", null, "jar", null)) + .build(); + + POMOperator.modify(projectModel); + } +} diff --git a/pom-operator/java-sample/src/test/resources/io/github/pixee/it/pom.xml b/pom-operator/java-sample/src/test/resources/io/github/pixee/it/pom.xml new file mode 100644 index 000000000..b653bbdf6 --- /dev/null +++ b/pom-operator/java-sample/src/test/resources/io/github/pixee/it/pom.xml @@ -0,0 +1,148 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + org.dom4j + dom4j + 2.1.3 + + + jaxen + jaxen + 1.2.0 + + + xerces + xercesImpl + 2.12.1 + + + io.github.java-diff-utils + java-diff-utils + 4.9 + + + junit + junit + 4.11 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + + diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractCommand.kt new file mode 100644 index 000000000..c70105712 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractCommand.kt @@ -0,0 +1,61 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.findOutIfUpgradeIsNeeded +import io.github.pixee.maven.operator.Util.selectXPathNodes +import io.github.pixee.maven.operator.Util.upgradeVersionNode +import org.dom4j.Element +import java.io.File + +/** + * Base implementation of Command - used by SimpleDependency and SimpleInsert + */ +abstract class AbstractCommand : Command { + /** + * Given a POM, locate its coordinates for a given dependency based on lookupExpression and figures out the upgrade + * + * TODO review this + */ + protected fun handleDependency(pm: ProjectModel, lookupExpression: String): Boolean { + val dependencyNodes = pm.pomFile.resultPom.selectXPathNodes(lookupExpression) + + if (1 == dependencyNodes.size) { + val versionNodes = dependencyNodes[0].selectXPathNodes("./m:version") + + if (1 == versionNodes.size) { + val versionNode = versionNodes[0] as Element + + var mustUpgrade = true + + if (pm.skipIfNewer) { + mustUpgrade = findOutIfUpgradeIsNeeded(pm, versionNode) + } + + if (mustUpgrade) { + upgradeVersionNode(pm, versionNode, pm.pomFile) + } + + return true + } + } + + return false + } + + override fun execute(pm: ProjectModel): Boolean = false + + override fun postProcess(c: ProjectModel): Boolean = false + + protected fun getLocalRepositoryPath(pm: ProjectModel): File { + val localRepositoryPath: File = when { + pm.repositoryPath != null -> pm.repositoryPath + System.getenv("M2_REPO") != null -> File(System.getenv("M2_REPO")) + System.getProperty("maven.repo.local") != null -> File(System.getProperty("maven.repo.local")) + else -> File( + System.getProperty("user.home"), + ".m2/repository" + ) + } + + return localRepositoryPath + } +} diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractQueryCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractQueryCommand.kt new file mode 100644 index 000000000..1ca268161 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/AbstractQueryCommand.kt @@ -0,0 +1,206 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.which +import org.apache.commons.lang3.SystemUtils +import org.apache.maven.shared.invoker.DefaultInvocationRequest +import org.apache.maven.shared.invoker.InvocationRequest +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File +import java.nio.file.Paths +import java.util.* + +/** + * Common Base Class - Meant to be used by Simple Queries using either Invoker and/or Embedder, thus + * relying on dependency:tree mojo outputting into a text file - which might be cached. + * + */ +abstract class AbstractQueryCommand : AbstractCommand() { + /** + * Generates a temporary file path used to store the output of the
dependency:tree
mojo + * + * @param pomFilePath POM Original File Path + */ + private fun getOutputPath(pomFilePath: File): File { + val basePath = pomFilePath.parentFile + + val outputBasename = "output-%08X.txt".format(pomFilePath.hashCode()) + + val outputPath = File(basePath, outputBasename) + + return outputPath + } + + /** + * Given a POM URI, returns a File Object + * + * @param d POMDocument + */ + protected fun getPomFilePath(d: POMDocument): File = Paths.get(d.pomPath!!.toURI()).toFile() + + /** + * Abstract Method to extract dependencies + * + * @param outputPath Output Path to where to store the content + * @param pomFilePath Input Pom Path + * @param c Project Model + */ + abstract fun extractDependencyTree(outputPath: File, pomFilePath: File, c: ProjectModel) + + /** + * Internal Holder Variable + * + * Todo: OF COURSE IT BREAKS THE PROTOCOL + */ + internal var result: Collection? = null + + /** + * We declare the main logic here - details are made in the child classes for now + */ + + override fun execute(pm: ProjectModel): Boolean { + val pomFilePath = getPomFilePath(pm.pomFile) + + val outputPath = getOutputPath(pomFilePath) + + if (outputPath.exists()) { + outputPath.delete() + } + + try { + extractDependencyTree(outputPath, pomFilePath, pm) + } catch (e: InvalidContextException) { + return false + } + + this.result = extractDependencies(outputPath).values + + return true + } + + /** + * Given a File containing the output of the dependency:tree mojo, read its contents and parse, creating an array of dependencies + * + * About the file contents: We receive something such as this, then filter it out: + * + *
+     *     br.com.ingenieux:pom-operator:jar:0.0.1-SNAPSHOT
+     *     +- xerces:xercesImpl:jar:2.12.1:compile
+     *     |  \- xml-apis:xml-apis:jar:1.4.01:compile
+     *     \- org.jetbrains.kotlin:kotlin-test:jar:1.5.31:test
+     * 
+ * + * @param outputPath file to read + */ + protected fun extractDependencies(outputPath: File) = outputPath.readLines().drop(1).map { + it.trim(*"+-|\\ ".toCharArray()) + }.map { + it to it.split(':') + }.associate { (line, elements) -> + val (groupId, artifactId, packaging, version, scope) = elements + + line to Dependency( + groupId = groupId, + artifactId = artifactId, + version = version, + packaging = packaging, + scope = scope + ) + } + + protected fun buildInvocationRequest( + outputPath: File, + pomFilePath: File, + c: ProjectModel + ): InvocationRequest { + val props = Properties(System.getProperties()).apply { + setProperty("outputFile", outputPath.absolutePath) + + val localRepositoryPath = getLocalRepositoryPath(c).absolutePath + + setProperty("maven.repo.local", localRepositoryPath) + } + + val request: InvocationRequest = DefaultInvocationRequest().apply { + findMaven(this) + + pomFile = pomFilePath + + isShellEnvironmentInherited = true + + isNoTransferProgress = true + isBatchMode = true + isRecursive = false + profiles = c.activeProfiles.toList() + isDebug = true + + isOffline = c.offline + + properties = props + + goals = listOf(DEPENDENCY_TREE_MOJO_REFERENCE) + } + + return request + } + + /** + * Locates where Maven is at - HOME var and main launcher script. + * + * @param invocationRequest InvocationRequest to be filled up + */ + private fun findMaven(invocationRequest: InvocationRequest) { + /* + * Step 1: Locate Maven Home + */ + val m2homeEnvVar = System.getenv("M2_HOME") + + if (null != m2homeEnvVar) { + val m2HomeDir = File(m2homeEnvVar) + + if (m2HomeDir.isDirectory) + invocationRequest.mavenHome = m2HomeDir + } + + /** + * Step 1.1: Try to guess if thats the case + */ + if (invocationRequest.mavenHome == null) { + val inferredHome = File(SystemUtils.getUserHome(), ".m2") + + if (!(inferredHome.exists() && inferredHome.isDirectory)) { + LOGGER.warn( + "Inferred User Home - which does not exist or not a directory: {}", + inferredHome + ) + } + + invocationRequest.mavenHome = inferredHome + } + + /** + * Step 2: Find Maven Executable given the operating system and PATH variable contents + */ + val foundExecutable = listOf("mvn", "mvnw").map { which(it) }.firstOrNull() + + if (null != foundExecutable) { + invocationRequest.mavenExecutable = foundExecutable + + return + } + + throw IllegalStateException("Missing Maven Home / Executable") + } + + companion object { + /** + * Mojo Reference + */ + const val DEPENDENCY_TREE_MOJO_REFERENCE = + "org.apache.maven.plugins:maven-dependency-plugin:3.3.0:tree" + + val LOGGER: Logger = LoggerFactory.getLogger(AbstractQueryCommand::class.java) + } + + override fun postProcess(c: ProjectModel): Boolean = false +} diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Chain.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Chain.kt new file mode 100644 index 000000000..67bd4124f --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Chain.kt @@ -0,0 +1,114 @@ +package io.github.pixee.maven.operator + +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +/** + * Implements a Chain of Responsibility Pattern + * + * @constructor commands: Commands to Use + */ +class Chain(vararg commands: Command) { + /** + * Internal ArrayList of the Commands + */ + internal val commandList: MutableList = ArrayList(commands.toList()) + + /** + * Executes the Commands in the Chain of Responsibility + * + * @param c ProjectModel (context) + * @return Boolean if successful + */ + fun execute(c: ProjectModel): Boolean { + var done = false + val listIterator = commandList.listIterator() + + while ((!done) && listIterator.hasNext()) { + val nextCommand = listIterator.next() + + done = nextCommand.execute(c) + + if (done) { + if (c.queryType == QueryType.NONE && (nextCommand !is SupportCommand)) { + c.modifiedByCommand = true + } + + break + } + } + + val result = done + + /** + * Goes Reverse Order applying the filter pattern + */ + + while (listIterator.previousIndex() > 0) { + val nextCommand = listIterator.previous() + + done = nextCommand.postProcess(c) + + if (done) + break + } + + return result + } + + companion object { + private val LOGGER: Logger = LoggerFactory.getLogger(Chain::class.java) + + /** + * Some classes won't have all available dependencies on the classpath during runtime + * for this reason we'll use
Class.forName
and report issues creating + */ + val AVAILABLE_QUERY_COMMANDS = listOf( + QueryType.SAFE to "QueryByResolver", + QueryType.SAFE to "QueryByParsing", + QueryType.UNSAFE to "QueryByEmbedder", + QueryType.UNSAFE to "QueryByInvoker", + ) + + /** + * Returns a Pre-Configured Chain with the Defaults for Modifying a POM + */ + fun createForModify() = + Chain( + CheckDependencyPresent, + CheckParentPackaging, + FormatCommand(), + DiscardFormatCommand(), + CompositeDependencyManagement(), + SimpleUpgrade, + SimpleDependencyManagement, + SimpleInsert + ) + + /** + * returns a pre-configured chain with the defaults for Querying + */ + fun createForQuery(queryType: QueryType = QueryType.SAFE): Chain { + val filteredCommands: List = AVAILABLE_QUERY_COMMANDS + .filter { it.first == queryType }.mapNotNull { + val commandClassName = "io.github.pixee.maven.operator.${it.second}" + + try { + Class.forName(commandClassName).newInstance() as Command + } catch (e: Throwable) { + LOGGER.warn("Creating class '{}': ", commandClassName, e) + + null + } + } + .toList() + + val commands : List = listOf(CHECK_PARENT_DIR_COMMAND) + filteredCommands + + if (commands.isEmpty()) + throw IllegalStateException("Unable to load any available strategy for ${queryType.name}") + + return Chain(*commands.toTypedArray()) + } + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckDependencyPresent.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckDependencyPresent.kt new file mode 100644 index 000000000..777f76d39 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckDependencyPresent.kt @@ -0,0 +1,16 @@ +package io.github.pixee.maven.operator + +/** + * Guard Command Singleton use to validate required parameters + */ +val CheckDependencyPresent = object : AbstractCommand() { + override fun execute(pm: ProjectModel): Boolean { + /** + * CheckDependencyPresent requires a Dependency to be Present + */ + if (null == pm.dependency) + throw MissingDependencyException("Dependency must be present for modify") + + return false + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckLocalRepositoryDirCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckLocalRepositoryDirCommand.kt new file mode 100644 index 000000000..c25591c3c --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckLocalRepositoryDirCommand.kt @@ -0,0 +1,18 @@ +package io.github.pixee.maven.operator + +import java.io.File + +val CHECK_PARENT_DIR_COMMAND = object : AbstractQueryCommand() { + override fun extractDependencyTree(outputPath: File, pomFilePath: File, c: ProjectModel) = + throw InvalidContextException() + + override fun execute(c: ProjectModel): Boolean { + val localRepositoryPath = getLocalRepositoryPath(c) + + if (!localRepositoryPath.exists()) { + localRepositoryPath.mkdirs() + } + + return false + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckParentPackaging.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckParentPackaging.kt new file mode 100644 index 000000000..7a44cf14d --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CheckParentPackaging.kt @@ -0,0 +1,54 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.selectXPathNodes +import org.dom4j.Element +import org.dom4j.Text + +/** + * Guard Command Singleton use to validate required parameters + */ +val CheckParentPackaging = object : AbstractCommand() { + fun packagingTypePredicate(d: POMDocument, packagingType: String): Boolean { + val elementText = + d.pomDocument.rootElement.selectXPathNodes("/m:project/m:packaging/text()") + .firstOrNull() + + if (elementText is Text) { + return elementText.text.equals(packagingType) + } + + return false + } + + override fun execute(pm: ProjectModel): Boolean { + val wrongParentPoms = pm.parentPomFiles.filterNot { packagingTypePredicate(it, "pom") } + + if (wrongParentPoms.isNotEmpty()) { + throw WrongDependencyTypeException("wrong packaging type for parentPom") + } + + if (pm.parentPomFiles.isNotEmpty()) { + // check main pom file has a inheritance to one of the members listed + if (!hasValidParentAndPackaging(pm.pomFile)) { + throw WrongDependencyTypeException("invalid parent/packaging combo for main pomfile") + } + } + + // todo: test a->b->c + + return false + } + + private fun hasValidParentAndPackaging(pomFile: POMDocument): Boolean { + val parentNode = pomFile.pomDocument.rootElement.selectXPathNodes("/m:project/m:parent") + .firstOrNull() as Element? ?: return false + + val packagingText = + (pomFile.pomDocument.rootElement.selectXPathNodes("/m:project/m:packaging/text()") + .firstOrNull() as Text?)?.text ?: "jar" + + @Suppress("UnnecessaryVariable") val validPackagingType = packagingText.endsWith("ar") + + return validPackagingType + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Command.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Command.kt new file mode 100644 index 000000000..776ae41b1 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Command.kt @@ -0,0 +1,19 @@ +package io.github.pixee.maven.operator + +/** + * Represents a Command in a Chain of Responsibility Pattern + */ +interface Command { + /** + * Given a context, performs an operation + * + * @param pm Context (Project Model) to use + * @return true if the execution was successful *AND* the chain must end + */ + fun execute(pm: ProjectModel): Boolean + + /** + * Post Processing, implementing a Filter Pattern + */ + fun postProcess(c: ProjectModel): Boolean +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CompositeDependencyManagement.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CompositeDependencyManagement.kt new file mode 100644 index 000000000..aa712ca95 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/CompositeDependencyManagement.kt @@ -0,0 +1,125 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.addIndentedElement +import io.github.pixee.maven.operator.Util.selectXPathNodes +import org.dom4j.Element +import java.lang.IllegalStateException + +class CompositeDependencyManagement : AbstractCommand() { + override fun execute(pm: ProjectModel): Boolean { + /** + * Abort if not multi-pom + */ + if (pm.parentPomFiles.isEmpty()) { + return false + } + + var result = false + + /** + * TODO: Make it configurable / clear WHERE one should change it + */ + val parentPomFile = pm.parentPomFiles.last() + + // add dependencyManagement + + val dependencyManagementElement = + if (parentPomFile.resultPom.rootElement.elements("dependencyManagement").isEmpty()) { + parentPomFile.resultPom.rootElement.addIndentedElement( + parentPomFile, + "dependencyManagement" + ) + } else { + parentPomFile.resultPom.rootElement.element("dependencyManagement") + } + + val newDependencyManagementElement = modifyDependency( + parentPomFile, + Util.buildLookupExpressionForDependencyManagement(pm.dependency!!), + pm, + dependencyManagementElement, + dependencyManagementNode = true, + ) + + if (pm.useProperties) { + val newVersionNode = + newDependencyManagementElement?.addIndentedElement(parentPomFile, "version") + ?: throw IllegalStateException("newDependencyManagementElement is missing") + + val whereToUpgradeVersionProperty = parentPomFile + + Util.upgradeVersionNode(pm, newVersionNode, whereToUpgradeVersionProperty) + } + + // add dependency to pom - sans version + modifyDependency( + pm.pomFile, + Util.buildLookupExpressionForDependency(pm.dependency!!), + pm, + pm.pomFile.resultPom.rootElement, + dependencyManagementNode = false, + ) + + if (!result) { + result = pm.pomFile.dirty + } + + return result + } + + private fun modifyDependency( + pomFileToModify: POMDocument, + lookupExpressionForDependency: String, + c: ProjectModel, + parentElement: Element, + dependencyManagementNode: Boolean, + ): Element? { + val dependencyNodes = + pomFileToModify.resultPom.selectXPathNodes(lookupExpressionForDependency) + + if (1 == dependencyNodes.size) { + val versionNodes = dependencyNodes[0].selectXPathNodes("./m:version") + + if (1 == versionNodes.size) { + val versionNode = versionNodes.first() + + versionNode.parent.content().remove(versionNode) + + pomFileToModify.dirty = true + } + + return dependencyNodes[0] as Element + } else { + val dependenciesNode: Element = + if (null != parentElement.element("dependencies")) { + parentElement.element("dependencies") + } else { + parentElement.addIndentedElement( + pomFileToModify, + "dependencies" + ) + } + + val dependencyNode: Element = + dependenciesNode.addIndentedElement(pomFileToModify, "dependency") + + dependencyNode.addIndentedElement(pomFileToModify, "groupId").text = + c.dependency!!.groupId + dependencyNode.addIndentedElement(pomFileToModify, "artifactId").text = + c.dependency!!.artifactId + + if (dependencyManagementNode) { + if (!c.useProperties) { + dependencyNode.addIndentedElement(pomFileToModify, "version").text = + c.dependency!!.version!! + } + } + + pomFileToModify.dirty = true + + return dependencyNode + } + + return null + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Dependency.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Dependency.kt new file mode 100644 index 000000000..b420b1c62 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Dependency.kt @@ -0,0 +1,30 @@ +package io.github.pixee.maven.operator + +/** + * Represents a Dependency + */ +data class Dependency( + val groupId: String, + val artifactId: String, + val version: String? = null, + val classifier: String? = null, + val packaging: String? = "jar", + val scope: String? = "compile", +) { + override fun toString(): String { + return listOf(groupId, artifactId, packaging, version).joinToString(":") + } + /** + * Given a string, parses - and creates - a new dependency Object + */ + companion object { + fun fromString(str: String): Dependency { + val elements = str.split(":") + + if (elements.size < 3) + throw IllegalStateException("Give me at least 3 elements") + + return Dependency(elements[0], elements[1], elements[2]) + } + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/DiscardFormatCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/DiscardFormatCommand.kt new file mode 100644 index 000000000..acb8dc87b --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/DiscardFormatCommand.kt @@ -0,0 +1,41 @@ +package io.github.pixee.maven.operator + +import org.xmlunit.builder.DiffBuilder +import org.xmlunit.builder.Input + +/** + * Command Class to Short-Circuit/Discard Processing when no pom changes were made + */ +class DiscardFormatCommand : AbstractCommand() { + override fun postProcess(pm: ProjectModel): Boolean { + var mustSkip = false + + for (pomFile in pm.allPomFiles) { + val originalDoc = Input.fromString(String(pomFile.originalPom)).build() + val modifiedDoc = Input.fromString(pomFile.resultPom.asXML()).build() + + val diff = DiffBuilder.compare(originalDoc).withTest(modifiedDoc) + .ignoreWhitespace() + .ignoreComments() + .ignoreElementContentWhitespace() + .checkForSimilar() + .build() + + val hasDifferences = diff.hasDifferences() + + if (!(pm.modifiedByCommand || hasDifferences)) { + pomFile.resultPomBytes = pomFile.originalPom + + mustSkip = true + } + } + + /** + * Triggers early abandonment + */ + if (mustSkip) + return true + + return super.postProcess(pm) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/EmbedderFacade.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/EmbedderFacade.kt new file mode 100644 index 000000000..baa5b33c8 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/EmbedderFacade.kt @@ -0,0 +1,146 @@ +package io.github.pixee.maven.operator + +import org.apache.maven.model.building.* +import org.apache.maven.project.ProjectModelResolver +import org.apache.maven.repository.internal.MavenRepositorySystemUtils +import org.eclipse.aether.DefaultRepositorySystemSession +import org.eclipse.aether.RepositorySystem +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory +import org.eclipse.aether.impl.DefaultServiceLocator +import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager +import org.eclipse.aether.repository.LocalRepository +import org.eclipse.aether.repository.RemoteRepository +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory +import org.eclipse.aether.spi.connector.transport.TransporterFactory +import org.eclipse.aether.transport.file.FileTransporterFactory +import org.eclipse.aether.transport.http.HttpTransporterFactory +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File + +data class EmbedderFacadeRequest( + val offline: Boolean, + val localRepositoryPath: File? = null, + val pomFile: File?, + val activeProfileIds: Collection = emptyList(), + val inactiveProfileIds: Collection = emptyList(), +) + +data class EmbedderFacadeResponse( + val modelBuildingResult: ModelBuildingResult, + val session: DefaultRepositorySystemSession?, + val repositorySystem: RepositorySystem?, + val remoteRepositories: List = emptyList(), +) + +object EmbedderFacade { + // Embedder Impl + + fun invokeEmbedder(req: EmbedderFacadeRequest): EmbedderFacadeResponse { + val localRepoPath: File = when { + req.localRepositoryPath != null -> req.localRepositoryPath + System.getenv("M2_REPO") != null -> File(System.getenv("M2_REPO")) + System.getProperty("maven.repo.local") != null -> File(System.getProperty("maven.repo.local")) + else -> File( + System.getProperty("user.home"), + ".m2/repository" + ) + } + + val localRepository = LocalRepository(localRepoPath.absolutePath) + + val locator: DefaultServiceLocator = + MavenRepositorySystemUtils.newServiceLocator() + + locator.addService( + RepositoryConnectorFactory::class.java, + BasicRepositoryConnectorFactory::class.java + ) + + locator.addService( + TransporterFactory::class.java, + FileTransporterFactory::class.java + ) + + locator.addService( + TransporterFactory::class.java, + HttpTransporterFactory::class.java + ) + + locator.setErrorHandler(object : + DefaultServiceLocator.ErrorHandler() { + override fun serviceCreationFailed( + type: Class<*>?, + impl: Class<*>?, + exception: Throwable + ) { + LOGGER.error( + "Service creation failed for {} with implementation {}", + type, impl, exception + ) + } + }) + + val repositorySystem = locator.getService(RepositorySystem::class.java) + + val session = MavenRepositorySystemUtils.newSession() + + session.localRepositoryManager = + repositorySystem.newLocalRepositoryManager(session, localRepository) + + session.setOffline(req.offline) + + val modelBuilder = DefaultModelBuilderFactory().newInstance() + + val repositoryManager = DefaultRemoteRepositoryManager() + + val remoteRepository = + RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/") + .build() + + val remoteRepositories = if (req.offline) { + emptyList() + } else { + listOf(remoteRepository) + } + + val modelBuildingRequest = DefaultModelBuildingRequest().apply { + this.userProperties = System.getProperties() + this.systemProperties = System.getProperties() + this.pomFile = req.pomFile!! + + this.isProcessPlugins = false + + this.modelSource = FileModelSource(pomFile) + + val modelResolver = ProjectModelResolver( + session, + null, + repositorySystem, + repositoryManager, + remoteRepositories, + null, + null + ) + + this.modelResolver = modelResolver + } + + val modelBuildingResult: ModelBuildingResult = try { + modelBuilder.build(modelBuildingRequest) + } catch (e: ModelBuildingException) { + LOGGER.warn("Oops: ", e) + + throw e + } + + return EmbedderFacadeResponse( + modelBuildingResult = modelBuildingResult, + session = session, + repositorySystem = repositorySystem, + remoteRepositories = remoteRepositories, + ) + } + + private val LOGGER: Logger = LoggerFactory.getLogger(EmbedderFacade::class.java) +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/FormatCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/FormatCommand.kt new file mode 100644 index 000000000..38fab6f95 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/FormatCommand.kt @@ -0,0 +1,341 @@ +package io.github.pixee.maven.operator + +import org.apache.commons.lang3.StringUtils +import org.mozilla.universalchardet.UniversalDetector +import java.io.StringWriter +import java.nio.charset.Charset +import java.util.* +import javax.xml.stream.XMLInputFactory +import javax.xml.stream.XMLOutputFactory +import javax.xml.stream.events.Characters +import javax.xml.stream.events.EndElement +import javax.xml.stream.events.StartDocument +import javax.xml.stream.events.StartElement + +/** + * Data Class used to keep track of matches (ranges, content, referring tag name) + */ +data class MatchData( + val range: IntRange, + val content: String, + val elementName: String +) + +/** + * This Command handles Formatting - particularly storing the original document preamble (the Processing Instruction and the first XML Element contents), + * which are the only ones which are tricky to format (due to element and its attributes being freeform - thus formatting lost when serializing the DOM + * and the PI being completely optional for the POM Document) + */ +class FormatCommand : AbstractCommand() { + /** + * StAX InputFactory + */ + private val inputFactory = XMLInputFactory.newInstance() + + /** + * StAX OutputFactory + */ + private val outputFactory = XMLOutputFactory.newInstance() + + override fun execute(pm: ProjectModel): Boolean { + for (pomFile in pm.allPomFiles) { + parseXmlAndCharset(pomFile) + + pomFile.endl = parseLineEndings(pomFile) + pomFile.indent = guessIndent(pomFile) + } + + return super.execute(pm) + } + + /** + * This one is quite fun yet important. Let me explain: + * + * The DOM doesn't track records if empty elements are either `` or ``. Therefore we need to scan all ocurrences of + * singleton elements. + * + * Therefore we use a bitSet to keep track of each element and offset, scanning it forward + * when serializing we pick backwards and rewrite tags accordingly + * + * @param doc Raw Document Bytes + * @see RE_EMPTY_ELEMENT + * @return bitSet of + * + */ + private fun elementBitSet(doc: ByteArray): BitSet { + val result = BitSet() + val eventReader = inputFactory.createXMLEventReader(doc.inputStream()) + val eventContent = StringWriter() + val xmlEventWriter = outputFactory.createXMLEventWriter(eventContent) + + while (eventReader.hasNext()) { + val next = eventReader.nextEvent() + + if (next is StartElement || next is EndElement) { + val startIndex = next.location.characterOffset + + eventContent.buffer.setLength(0) + + xmlEventWriter.add(next) + xmlEventWriter.flush() + + val endIndex = startIndex + eventContent.buffer.length + + result.set(startIndex, startIndex + endIndex) + } + } + + return result + } + + /** + * Returns a reverse-ordered list of all the single element matches from the pom document + * raw string + * + * this is important so we can mix and match offsets and apply formatting accordingly + * + * @param xmlDocumentString Rendered POM Document Contents (string-formatted) + * @return map of (index, matchData object) reverse ordered + */ + private fun findSingleElementMatchesFrom(xmlDocumentString: String) = + RE_EMPTY_ELEMENT.findAll(xmlDocumentString).map { + it.range.first to MatchData( + range = it.range, + content = it.value, + elementName = ((it.groups[1]?.value ?: it.groups[2]?.value)!!) + ) + }.sortedByDescending { it.first }.toMap(LinkedHashMap()) + + /** + * Guesses the indent character (spaces / tabs) and length from the original document + * formatting settings + * + * @param pomFile (project model) where it takes its input pom + * @return indent string + */ + private fun guessIndent(pomFile: POMDocument): String { + val eventReader = inputFactory.createXMLEventReader(pomFile.originalPom.inputStream()) + + val freqMap: MutableMap = mutableMapOf() + val charFreqMap: MutableMap = mutableMapOf() + + /** + * Parse, while grabbing whitespace sequences and examining it + */ + while (eventReader.hasNext()) { + val event = eventReader.nextEvent() + + if (event is Characters) { + if (StringUtils.isWhitespace(event.asCharacters().data)) { + val patterns = event.asCharacters().data.split(*LINE_ENDINGS.toTypedArray()) + + /** + * Updates space / character frequencies found + */ + val blankPatterns = patterns + .filter { it.isNotEmpty() } + .filter { StringUtils.isAllBlank(it) } + + blankPatterns + .map { it to it.length } + .forEach { + freqMap.merge(it.second, 1) { a, b -> a + b } + } + + blankPatterns.map { it[0] } + .forEach { + charFreqMap.merge(it, 1) { a, b -> + a + b + } + } + } + } + } + + /** + * Assign most frequent indent char + */ + val indentCharacter: Char = charFreqMap.entries.maxBy { it.value }.key + + /** + * Casts as a String + */ + val indentcharacterAsString = String(charArrayOf(indentCharacter)) + + /** + * Picks the length + */ + val indentLength = freqMap.entries.minBy { it.key }.key + + /** + * Builds the standard indent string (length vs char) + */ + val indentString = StringUtils.repeat(indentcharacterAsString, indentLength) + + /** + * Returns it + */ + return indentString + } + + private fun parseLineEndings(pomFile: POMDocument): String { + val str = String(pomFile.originalPom.inputStream().readBytes(), pomFile.charset) + + return LINE_ENDINGS.associateWith { str.split(it).size } + .maxBy { it.value } + .key + } + + private fun parseXmlAndCharset(pomFile: POMDocument) { + /** + * Performs a StAX Parsing to Grab the first element + */ + val eventReader = inputFactory.createXMLEventReader(pomFile.originalPom.inputStream()) + + var charset: Charset? = null + + /** + * Parse, while grabbing its preamble and encoding + */ + while (true) { + val event = eventReader.nextEvent() + + if (event.isStartDocument && (event as StartDocument).encodingSet()) { + /** + * Processing Instruction Found - Store its Character Encoding + */ + charset = Charset.forName(event.characterEncodingScheme) + } else if (event.isEndElement) { + /** + * First End of Element ("Tag") found - store its offset + */ + val endElementEvent = (event as EndElement) + + val offset = endElementEvent.location.characterOffset + + pomFile.preamble = + pomFile.originalPom.toString(pomFile.charset).substring(0, offset) + + break + } + + if (!eventReader.hasNext()) + throw IllegalStateException("Couldn't find document start") + } + + if (null == charset) { + val detectedCharsetName = + UniversalDetector.detectCharset(pomFile.originalPom.inputStream()) + + charset = Charset.forName(detectedCharsetName) + } + + pomFile.charset = charset!! + + val lastLine = String(pomFile.originalPom, pomFile.charset) + + val lastLineTrimmed = lastLine.trimEnd() + + pomFile.suffix = lastLine.substring(lastLineTrimmed.length) + } + + /** + * When doing the opposite, render the XML using the optionally supplied encoding (defaults to UTF8 obviously) + * but apply the original formatting as well + */ + override fun postProcess(pm: ProjectModel): Boolean { + for (pomFile in pm.allPomFiles) { + /** + * Serializes it back + */ + val content = serializePomFile(pomFile) + + pomFile.resultPomBytes = content + } + + return super.postProcess(pm) + } + + /** + * Serialize a POM Document + * + * @param pom pom document + * @return bytes for the pom document + */ + private fun serializePomFile(pom: POMDocument): ByteArray { + // Generate a String representation. We'll need to patch it up and apply back + // differences we recored previously on the pom (see the pom member variables) + var xmlRepresentation = pom.resultPom.asXML().toString() + + val originalElementMap = elementBitSet(pom.originalPom) + val targetElementMap = elementBitSet(xmlRepresentation.toByteArray()) + + // Let's find out the original empty elements from the original pom and store into a stack + val elementsToReplace: MutableList = ArrayList().apply { + val matches = + findSingleElementMatchesFrom(pom.originalPom.toString(pom.charset)).values + + val filteredMatches = matches.filter { originalElementMap[it.range.first] } + + this.addAll(filteredMatches) + } + + // Lets to the replacements backwards on the existing, current pom + val emptyElements = findSingleElementMatchesFrom(xmlRepresentation) + .filter { targetElementMap[it.value.range.first] } + + emptyElements.forEach { (_, match) -> + val nextMatch = elementsToReplace.removeFirst() + + xmlRepresentation = xmlRepresentation.replaceRange(match.range, nextMatch.content) + } + + /** + * We might need to replace the beginning of the POM with the same content + * from the very beginning + * + * Grab the same initial offset from the formatted element like we did + */ + val inputFactory = XMLInputFactory.newInstance() + val eventReader = inputFactory.createXMLEventReader( + xmlRepresentation.toByteArray(pom.charset).inputStream() + ) + + while (true) { + val event = eventReader.nextEvent() + + if (event.isEndElement) { + /** + * Apply the formatting and tweak its XML Representation + */ + val endElementEvent = (event as EndElement) + + val offset = endElementEvent.location.characterOffset + + xmlRepresentation = + pom.preamble + xmlRepresentation.substring(offset) + pom.suffix + + break + } + + /** + * This code shouldn't be unreachable at all + */ + if (!eventReader.hasNext()) + throw IllegalStateException("Couldn't find document start") + } + + /** + * Serializes it back from (string to ByteArray) + */ + val serializedContent = xmlRepresentation.toByteArray(pom.charset) + + return serializedContent + } + + companion object { + val LINE_ENDINGS = setOf("\r\n", "\n", "\r") + + val RE_EMPTY_ELEMENT = Regex("""<(\p{Alnum}+)>|<(\p{Alnum}+)\s*/>""") + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidContextException.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidContextException.kt new file mode 100644 index 000000000..12817b9ce --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidContextException.kt @@ -0,0 +1,6 @@ +package io.github.pixee.maven.operator + +/** + * This is an exception to tag when the output file couldn't be generated - perhaps due a missing or incompatible maven installation + */ +internal class InvalidContextException : RuntimeException() \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidPathException.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidPathException.kt new file mode 100644 index 000000000..b9fe110e1 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/InvalidPathException.kt @@ -0,0 +1,10 @@ +package io.github.pixee.maven.operator + +import java.io.File +import java.io.IOException + +class InvalidPathException( + val parentPath: File, + val relativePath: String, + val loop: Boolean = false +) : IOException("Invalid Relative Path $relativePath (from ${parentPath.absolutePath}) (loops? ${loop})") \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/MissingDependencyException.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/MissingDependencyException.kt new file mode 100644 index 000000000..757c89f14 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/MissingDependencyException.kt @@ -0,0 +1,3 @@ +package io.github.pixee.maven.operator + +class MissingDependencyException(message: String?) : RuntimeException(message) \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocument.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocument.kt new file mode 100644 index 000000000..4afebb2ac --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocument.kt @@ -0,0 +1,59 @@ +package io.github.pixee.maven.operator + +import org.dom4j.Document +import java.io.File +import java.net.URL +import java.nio.charset.Charset + +/** + * Data Class to Keep track of an entire POM File, including: + * + * Path (pomPath) + * + * DOM Contents (pomDocument) - original + * DOM Contents (resultPom) - modified + * + * Charset (ditto) + * Indent (ditto) + * Preamble (ditto) + * Suffix (ditto) + * Line Endings (endl) + * + * Original Content (originalPom) + * Modified Content (resultPomBytes) + */ +@Suppress("ArrayInDataClass") +data class POMDocument( + val originalPom: ByteArray, + val pomPath: URL?, + val pomDocument: Document, + var charset: Charset = Charset.defaultCharset(), + var endl: String = "\n", + var indent: String = " ", + var resultPomBytes: ByteArray = byteArrayOf(), + + /** + * Preamble Contents are stored here + */ + var preamble: String = "", + + /** + * Afterword - if needed + */ + var suffix: String = "", +) { + internal val file: File get() = File(this.pomPath!!.toURI()) + + val resultPom: Document = pomDocument.clone() as Document + + var dirty: Boolean = false + + override fun toString(): String { + return if (null == this.pomPath) { + "missing" + } else { + ("[POMDocument @ " + this.pomPath.toString() + "]") + } + } +} + diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocumentFactory.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocumentFactory.kt new file mode 100644 index 000000000..4f5a74bce --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMDocumentFactory.kt @@ -0,0 +1,36 @@ +package io.github.pixee.maven.operator + +import org.apache.commons.io.IOUtils +import org.dom4j.io.SAXReader +import java.io.File +import java.io.InputStream +import java.net.URL + +/** + * Factory for a POMDocument + */ +object POMDocumentFactory { + @JvmStatic + fun load(`is`: InputStream): POMDocument { + val originalPom: ByteArray = IOUtils.toByteArray(`is`) + val pomDocument = SAXReader().read(originalPom.inputStream())!! + + return POMDocument(originalPom = originalPom, pomDocument = pomDocument, pomPath = null) + } + + @JvmStatic + fun load(f: File) = + load(f.toURI().toURL()) + + @JvmStatic + fun load(url: URL): POMDocument { + val originalPom: ByteArray = IOUtils.toByteArray(url.openStream()) + + val saxReader = SAXReader() + + val pomDocument = saxReader.read(originalPom.inputStream()) + + return POMDocument(originalPom = originalPom, pomPath = url, pomDocument = pomDocument) + } + +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMOperator.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMOperator.kt new file mode 100644 index 000000000..85c5e8578 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMOperator.kt @@ -0,0 +1,52 @@ +package io.github.pixee.maven.operator + + +/** + * Façade for the POM Operator + */ +object POMOperator { + /** + * Bump a Dependency Version on a POM + * + * @param projectModel Project Model (Context) class + */ + @JvmStatic + fun modify(projectModel: ProjectModel) = Chain.createForModify().execute(projectModel) + + /** + * Public API - Query for all the artifacts referenced inside a POM File + * + * @param projectModel Project Model (Context) Class + */ + @JvmStatic + fun queryDependency( + projectModel: ProjectModel + ) = queryDependency(projectModel, emptyList()) + + /** + * Internal Use (package-wide) - Query for all the artifacts mentioned on a POM + * + * @param projectModel Project Model (Context) class + * @param commandList do not use (required for tests) + */ + @JvmStatic + internal fun queryDependency( + projectModel: ProjectModel, + commandList: List + ): Collection { + val chain = Chain.createForQuery(projectModel.queryType) + + if (commandList.isNotEmpty()) { + chain.commandList.clear() + chain.commandList.addAll(commandList) + } + + chain.execute(projectModel) + + val lastCommand = chain.commandList.filterIsInstance() + .lastOrNull { it.result != null } + ?: return emptyList() + + return lastCommand.result!! + } +} diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMScanner.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMScanner.kt new file mode 100644 index 000000000..6634f40fb --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/POMScanner.kt @@ -0,0 +1,151 @@ +package io.github.pixee.maven.operator + +import org.dom4j.Element +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import java.util.* +import kotlin.io.path.notExists + +object POMScanner { + private val LOGGER: Logger = LoggerFactory.getLogger(POMScanner::class.java) + + private val RE_WINDOWS_PATH = Regex("""^\p{Alpha}:""") + + @JvmStatic + fun scanFrom(originalFile: File, topLevelDirectory: File): ProjectModelFactory { + val originalDocument = ProjectModelFactory.load(originalFile) + + val parentPoms: List = try { + getParentPoms(originalFile) + } catch (e: Exception) { + LOGGER.warn("While trying embedder: ", e) + + return legacyScanFrom(originalFile, topLevelDirectory) + } + + return originalDocument + .withParentPomFiles(parentPoms.map { POMDocumentFactory.load(it) }) + } + + @JvmStatic + fun legacyScanFrom(originalFile: File, topLevelDirectory: File): ProjectModelFactory { + val pomFile: POMDocument = POMDocumentFactory.load(originalFile) + val parentPomFiles: MutableList = arrayListOf() + + val pomFileQueue: Queue = LinkedList() + + val relativePathElement = + pomFile.pomDocument.rootElement.element("parent")?.element("relativePath") + + if (relativePathElement != null && relativePathElement.textTrim.isNotEmpty()) { + pomFileQueue.add(relativePathElement) + } + + var lastFile: File = originalFile + + fun resolvePath(baseFile: File, relativePath: String): Path { + var parentDir = baseFile + + if (parentDir.isFile) { + parentDir = parentDir.parentFile + } + + val result = File(File(parentDir, relativePath).toURI().normalize().path) + + lastFile = if (result.isDirectory) { + result + } else { + result.parentFile + } + + return Paths.get(result.absolutePath) + } + + val prevPaths: MutableSet = linkedSetOf() + + while (pomFileQueue.isNotEmpty()) { + val relativePathElement = pomFileQueue.poll() + + if (relativePathElement.textTrim.isEmpty()) { + break + } + + val relativePath = fixPomRelativePath(relativePathElement.text) + + if (!isRelative(relativePath)) + throw InvalidPathException(pomFile.file, relativePath) + + if (prevPaths.contains(relativePath)) { + throw InvalidPathException(pomFile.file, relativePath, loop = true) + } else { + prevPaths.add(relativePath) + } + + val newPath = resolvePath(lastFile, relativePath) + + if (newPath.notExists()) + throw InvalidPathException(pomFile.file, relativePath) + + if (!newPath.startsWith(topLevelDirectory.absolutePath)) + throw InvalidPathException(pomFile.file, relativePath) + + val newPomFile = POMDocumentFactory.load(newPath.toFile()) + + parentPomFiles.add(newPomFile) + + val newRelativePathElement = + newPomFile.pomDocument.rootElement.element("parent")?.element("relativePath") + + if (newRelativePathElement != null) { + pomFileQueue.add(newRelativePathElement) + } + } + + return ProjectModelFactory.loadFor( + pomFile = pomFile, + parentPomFiles = parentPomFiles + ) + } + + private fun fixPomRelativePath(text: String?): String { + if (null == text) + return "" + + val name = File(text).name + + if (-1 == name.indexOf(".")) { + return "$text/pom.xml" + } + + return text + } + + private fun isRelative(path: String): Boolean { + if (path.matches(RE_WINDOWS_PATH)) { + return false + } + + return !(path.startsWith("/") || path.startsWith("~")) + } + + + private fun getParentPoms(originalFile: File): List { + val embedderFacadeResponse = EmbedderFacade.invokeEmbedder( + EmbedderFacadeRequest(offline = true, pomFile = originalFile) + ) + + val res = embedderFacadeResponse.modelBuildingResult + + val rawModels = res.modelIds.map { res.getRawModel(it) }.toList() + + val parentPoms: List = + if (rawModels.size > 1) { + rawModels.subList(1, rawModels.size).mapNotNull { it.pomFile }.toList() + } else + emptyList() + return parentPoms + } +} diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModel.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModel.kt new file mode 100644 index 000000000..73709090b --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModel.kt @@ -0,0 +1,131 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.selectXPathNodes +import org.dom4j.Element +import java.io.File + +/** + * ProjectModel represents the input parameters for the chain + * + * @todo consider resolution and also Topological Sort of Properties for cross-property reference + */ +class ProjectModel internal constructor( + val pomFile: POMDocument, + + val parentPomFiles: List = emptyList(), + + var dependency: Dependency?, + val skipIfNewer: Boolean, + val useProperties: Boolean, + val activeProfiles: Set, + val overrideIfAlreadyExists: Boolean, + val queryType: QueryType = QueryType.NONE, + + val repositoryPath: File? = null, + + val offline: Boolean = false, +) { + internal var modifiedByCommand = false + + /** + * Involved POM Files + */ + val allPomFiles: Collection + get() = listOfNotNull( + pomFile, + *parentPomFiles.toTypedArray() + ) + + val resolvedProperties = + run { + val result: MutableMap = LinkedHashMap() + + allPomFiles + .reversed() // parent first, children later - thats why its reversed + .forEach { pomFile -> + val rootProperties = + propertiesDefinedOnPomDocument(pomFile) + + result.putAll(rootProperties) + + val activatedProfiles = activeProfiles.filterNot { it.startsWith("!") } + + val newPropertiesFromProfiles = activatedProfiles.map { profileName -> + getPropertiesFromProfile(profileName, pomFile) + } + + newPropertiesFromProfiles.forEach { result.putAll(it) } + } + + result.toMap() + } + + val propertiesDefinedByFile: Map>> = + run { + val result: MutableMap>> = LinkedHashMap() + + allPomFiles + .reversed() + .forEach { pomFile -> + val rootProperties = + propertiesDefinedOnPomDocument(pomFile) + + val tempProperties: MutableMap = LinkedHashMap() + + tempProperties.putAll(rootProperties) + + val activatedProfiles = activeProfiles.filterNot { it.startsWith("!") } + + val newPropertiesFromProfiles = activatedProfiles.map { profileName -> + getPropertiesFromProfile(profileName, pomFile) + } + + newPropertiesFromProfiles.forEach { tempProperties.putAll(it) } + + tempProperties.entries.forEach { entry -> + if (!result.containsKey(entry.key)) { + result[entry.key] = ArrayList() + } + + val definitionList = + result[entry.key] as MutableList> + + definitionList.add(entry.value to pomFile) + } + } + + result + } + + private fun getPropertiesFromProfile( + profileName: String, + pomFile: POMDocument + ): Map { + val expression = + "/m:project/m:profiles/m:profile[./m:id[text()='${profileName}']]/m:properties" + val propertiesElements = + pomFile.pomDocument.selectXPathNodes(expression) + + val newPropertiesToAppend = + propertiesElements.filterIsInstance() + .flatMap { it.elements() } + .associate { + it.name to it.text + } + + return newPropertiesToAppend + } + + companion object { + fun propertiesDefinedOnPomDocument(pomFile: POMDocument): Map { + val rootProperties = + pomFile.pomDocument.rootElement.elements("properties") + .flatMap { it.elements() } + .associate { + it.name to it.text + } + return rootProperties + } + } +} + diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModelFactory.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModelFactory.kt new file mode 100644 index 000000000..80191385e --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/ProjectModelFactory.kt @@ -0,0 +1,151 @@ +package io.github.pixee.maven.operator + +import java.io.File +import java.io.InputStream +import java.net.URL + +/** + * Builder Object for ProjectModel instances + */ +class ProjectModelFactory private constructor( + private var pomFile: POMDocument, + private var parentPomFiles: List = listOf(), + private var dependency: Dependency? = null, + private var skipIfNewer: Boolean = false, + private var useProperties: Boolean = false, + private var activeProfiles: Set = emptySet(), + private var overrideIfAlreadyExists: Boolean = false, + private var queryType: QueryType = QueryType.NONE, + private var repositoryPath: File? = null, + private var offline: Boolean = false, +) { + /** + * Fluent Setter + * + * @param pomFile POM File + */ + fun withPomFile(pomFile: POMDocument): ProjectModelFactory = this.apply { + this.pomFile = pomFile + } + + /** + * Fluent Setter + * + * @param parentPomFiles Parent POM Files + */ + fun withParentPomFiles(parentPomFiles: Collection): ProjectModelFactory = + this.apply { + this.parentPomFiles = listOf(*parentPomFiles.filterNotNull().toTypedArray()) + } + + /** + * Fluent Setter + * + * @param dep dependency + */ + fun withDependency(dep: Dependency): ProjectModelFactory = this.apply { + this.dependency = dep + } + + /** + * Fluent Setter + */ + fun withSkipIfNewer(skipIfNewer: Boolean): ProjectModelFactory = this.apply { + this.skipIfNewer = skipIfNewer + } + + /** + * Fluent Setter + */ + fun withUseProperties(useProperties: Boolean): ProjectModelFactory = this.apply { + this.useProperties = useProperties + } + + /** + * Fluent Setter + */ + fun withActiveProfiles(vararg activeProfiles: String): ProjectModelFactory = this.apply { + this.activeProfiles = setOf(*activeProfiles) + } + + /** + * Fluent Setter + */ + fun withOverrideIfAlreadyExists(overrideIfAlreadyExists: Boolean) = this.apply { + this.overrideIfAlreadyExists = overrideIfAlreadyExists + } + + /** + * Fluent Setter + * + * @param queryType query type + */ + fun withQueryType(queryType: QueryType) = this.apply { + this.queryType = queryType + } + + /** + * Fluent Setter + * + * @param repositoryPath Repository Path + */ + fun withRepositoryPath(repositoryPath: File?) = this.apply { + this.repositoryPath = repositoryPath + } + + /** + * Fluent Setter + * + * @param offline Offline + */ + fun withOffline(offline: Boolean) = this.apply { + this.offline = offline + } + + /** + * Fluent Setter + */ + fun build(): ProjectModel { + return ProjectModel( + pomFile = pomFile, + parentPomFiles = parentPomFiles, + dependency = dependency, + skipIfNewer = skipIfNewer, + useProperties = useProperties, + activeProfiles = activeProfiles, + overrideIfAlreadyExists = overrideIfAlreadyExists, + queryType = queryType, + repositoryPath = repositoryPath, + offline = offline, + ) + } + + /** + * Mostly Delegates to POMDocumentFactory + */ + companion object { + @JvmStatic + fun load(`is`: InputStream): ProjectModelFactory { + val pomDocument = POMDocumentFactory.load(`is`) + + return ProjectModelFactory(pomFile = pomDocument) + } + + @JvmStatic + fun load(f: File) = + load(f.toURI().toURL()) + + @JvmStatic + fun load(url: URL): ProjectModelFactory { + val pomFile = POMDocumentFactory.load(url) + + return ProjectModelFactory(pomFile = pomFile) + } + + @JvmStatic + internal fun loadFor( + pomFile: POMDocument, + parentPomFiles: Collection, + ) = ProjectModelFactory(pomFile = pomFile, parentPomFiles = parentPomFiles.toList()) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByEmbedder.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByEmbedder.kt new file mode 100644 index 000000000..973f06852 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByEmbedder.kt @@ -0,0 +1,80 @@ +package io.github.pixee.maven.operator + +import org.apache.commons.io.output.NullOutputStream +import org.apache.maven.cli.MavenCli +import org.apache.maven.shared.invoker.InvocationRequest +import org.apache.maven.shared.invoker.MavenCommandLineBuilder +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream + +/** + * Uses Maven Embedder to Implement + */ +class QueryByEmbedder : AbstractQueryCommand() { + /** + * Runs the "dependency:tree" mojo - but using Embedder instead. + */ + override fun extractDependencyTree(outputPath: File, pomFilePath: File, c: ProjectModel) { + val mavenCli = MavenCli() + + val cliBuilder = MavenCommandLineBuilder() + + val invocationRequest: InvocationRequest = + buildInvocationRequest(outputPath, pomFilePath, c) + + val oldMultimoduleValue = System.getProperty(MAVEN_MULTIMODULE_PROJECT_DIRECTORY) + + System.setProperty(MAVEN_MULTIMODULE_PROJECT_DIRECTORY, pomFilePath.parent) + + try { + val cliBuilderResult = cliBuilder.build(invocationRequest) + + val cliArgs = cliBuilderResult.commandline.toList().drop(1).toTypedArray() + + val baosOut = + if (LOGGER.isDebugEnabled) ByteArrayOutputStream() else NullOutputStream.NULL_OUTPUT_STREAM + + val baosErr = + if (LOGGER.isDebugEnabled) ByteArrayOutputStream() else NullOutputStream.NULL_OUTPUT_STREAM + + val result: Int = mavenCli.doMain( + cliArgs, + pomFilePath.parent, + PrintStream(baosOut, true), + PrintStream(baosErr, true) + ) + + if (LOGGER.isDebugEnabled) { + LOGGER.debug("baosOut: {}", baosOut.toString()) + LOGGER.debug("baosErr: {}", baosErr.toString()) + } + + /** + * Sometimes the Embedder will fail - it will return this specific exit code (1) as well as + * not generate this file + * + * If that happens, we'll move to the next strategy (Invoker-based likely) by throwing a + * custom exception which is caught inside the Chain#execute method + * + * @see Chain#execute + */ + if (1 == result && (!outputPath.exists())) + throw InvalidContextException() + + if (0 != result) + throw IllegalStateException("Unexpected status code: %02d".format(result)) + } finally { + if (null != oldMultimoduleValue) + System.setProperty(MAVEN_MULTIMODULE_PROJECT_DIRECTORY, oldMultimoduleValue) + } + } + + companion object { + const val MAVEN_MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory" + + private val LOGGER: Logger = LoggerFactory.getLogger(QueryByEmbedder::class.java) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByInvoker.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByInvoker.kt new file mode 100644 index 000000000..a3fb973d2 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByInvoker.kt @@ -0,0 +1,32 @@ +package io.github.pixee.maven.operator + +import org.apache.maven.shared.invoker.DefaultInvoker +import org.apache.maven.shared.invoker.InvocationRequest +import java.io.File + +class QueryByInvoker : AbstractQueryCommand() { + + override fun extractDependencyTree( + outputPath: File, + pomFilePath: File, + c: ProjectModel + ) { + val invoker = DefaultInvoker() + + val invocationRequest: InvocationRequest = + buildInvocationRequest(outputPath, pomFilePath, c) + + val invocationResult = invoker.execute(invocationRequest) + + val exitCode = invocationResult.exitCode + + if (0 != exitCode) { + throw IllegalStateException( + "Unexpected Status Code from Invoker: %02d".format( + exitCode + ) + ) + } + } + +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByParsing.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByParsing.kt new file mode 100644 index 000000000..617ba48d9 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByParsing.kt @@ -0,0 +1,165 @@ +@file:Suppress("DEPRECATION") + +package io.github.pixee.maven.operator + +import org.apache.commons.lang3.builder.CompareToBuilder +import org.apache.commons.lang3.text.StrSubstitutor +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File +import java.util.* + +/** + * this one is a bit more complex, as it intents to to a "best effort" attempt at parsing a pom + * focusing only on dependency right now, * without relying to any maven infrastructure at all + */ +class QueryByParsing : AbstractQueryCommand() { + override fun extractDependencyTree(outputPath: File, pomFilePath: File, c: ProjectModel) { + TODO("Not yet implemented") + } + + private val dependencies: MutableSet = LinkedHashSet() + + private val dependencyManagement: MutableSet = + TreeSet(object : Comparator { + override fun compare(o1: Dependency?, o2: Dependency?): Int { + if (o1 == o2) + return 0 + + if (o1 == null) + return 1 + + if (o2 == null) + return -1 + + return CompareToBuilder().append(o1.groupId, o2.groupId) + .append(o1.artifactId, o2.artifactId).toComparison() + } + }) + + private val properties: MutableMap = LinkedHashMap() + + private val strSubstitutor = StrSubstitutor(properties) + + override fun execute(pm: ProjectModel): Boolean { + /** + * Enlist all pom files given an hierarchy + */ + val pomFilesByHierarchy = pm.allPomFiles.reversed() + + for (pomDocument in pomFilesByHierarchy) { + updateProperties(pomDocument) + + updateDependencyManagement(pomDocument) + + updateDependencies(pomDocument) + } + + this.result = dependencies + + return true + } + + private fun updateDependencyManagement(pomDocument: POMDocument) { + val dependencyManagementDependenciesToAdd: Collection = + pomDocument.pomDocument.// + rootElement. // + element("dependencyManagement")?. // + element("dependencies")?. // + elements("dependency")?. // + map { dependencyElement -> + val groupId = dependencyElement.element("groupId").text + val artifactId = dependencyElement.element("artifactId").text + + val version = dependencyElement.element("version")?.text ?: "UNKNOWN" + + val classifier = dependencyElement.element("classifier")?.text + val packaging = dependencyElement.element("packaging")?.text + + val versionInterpolated = try { + strSubstitutor.replace(version) + } catch (e: java.lang.IllegalStateException) { + LOGGER.warn("while interpolating version", e) + + "UNKNOWN" + } + + Dependency(groupId, artifactId, versionInterpolated, classifier, packaging) + }?.toList() ?: emptyList() + + this.dependencyManagement.addAll(dependencyManagementDependenciesToAdd) + } + + fun lookForDependencyManagement(groupId: String, artifactId: String): Dependency? = + this.dependencyManagement.firstOrNull { it.groupId == groupId && it.artifactId == artifactId } + + private fun updateDependencies(pomDocument: POMDocument) { + val dependenciesToAdd: Collection = + pomDocument.pomDocument.// + rootElement. // + element("dependencies")?. // + elements("dependency")?. // + map { dependencyElement -> + val groupId = dependencyElement.element("groupId").text + val artifactId = dependencyElement.element("artifactId").text + + val versionElement = dependencyElement.element("version") + + val proposedDependency = lookForDependencyManagement(groupId, artifactId) + + if (versionElement == null && null != proposedDependency) { + proposedDependency + } else { + val version = versionElement?.text ?: "UNKNOWN" + + val classifier = dependencyElement.element("classifier")?.text + val packaging = dependencyElement.element("packaging")?.text + + val versionInterpolated = try { + strSubstitutor.replace(version) + } catch (e: java.lang.IllegalStateException) { + LOGGER.warn("while interpolating version", e) + + "UNKNOWN" + } + + Dependency(groupId, artifactId, versionInterpolated, classifier, packaging) + } + }?.toList() ?: emptyList() + + this.dependencies.addAll( + dependenciesToAdd + ) + } + + /** + * Updates the Properties member variable based on whats on the POMDocument + */ + private fun updateProperties(pomDocument: POMDocument) { + val propsDefined = ProjectModel.propertiesDefinedOnPomDocument(pomDocument) + + propsDefined.entries.filterNot { it.value.matches(RE_INTERPOLATION) } + .forEach { + properties[it.key] = it.value + } + + propsDefined.entries.filterNot { it.value.matches(RE_INTERPOLATION) } + .forEach { + val newValue = try { + strSubstitutor.replace(it.value) + } catch (e: IllegalStateException) { + LOGGER.warn("while replacing variables: ", e) + + it.value + } + + properties.put(it.key, it.value) + } + } + + companion object { + val RE_INTERPOLATION = Regex(""".*\$\{[\p{Alnum}.-_]+\}.*""") + + val logger: Logger = LoggerFactory.getLogger(QueryByParsing::class.java) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByResolver.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByResolver.kt new file mode 100644 index 000000000..a54094f59 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryByResolver.kt @@ -0,0 +1,115 @@ +package io.github.pixee.maven.operator + +import org.apache.maven.model.building.ModelBuildingException +import org.eclipse.aether.artifact.DefaultArtifact +import org.eclipse.aether.collection.CollectRequest +import org.eclipse.aether.collection.DependencyCollectionException +import org.eclipse.aether.graph.DependencyNode +import org.eclipse.aether.graph.DependencyVisitor +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File + +/** + * This is a resolver that actually embeds much of Maven Logic into that. + * + * Futurely TODO Support Third Party / User-Supplied Repositories (right now it only supports central) + */ +class QueryByResolver : AbstractQueryCommand() { + companion object { + private val LOGGER: Logger = LoggerFactory.getLogger(QueryByResolver::class.java) + } + + override fun extractDependencyTree(outputPath: File, pomFilePath: File, c: ProjectModel) { + TODO("Not yet implemented") + } + + override fun execute(pm: ProjectModel): Boolean { + val req = EmbedderFacadeRequest( + offline = pm.offline, + pomFile = pm.pomFile.file, + localRepositoryPath = pm.repositoryPath, + activeProfileIds = pm.activeProfiles.filterNot { it.startsWith("!") }.toList(), + inactiveProfileIds = pm.activeProfiles.filter { it.startsWith("!") } + .map { it.substring(1) }.toList() + ) + + this.result = emptyList() + + val embedderFacadeResponse: EmbedderFacadeResponse + + try { + embedderFacadeResponse = EmbedderFacade.invokeEmbedder(req) + } catch (mbe: ModelBuildingException) { + LOGGER.warn("Oops:", mbe) + + return false + } + + val res = embedderFacadeResponse.modelBuildingResult + + val dependencyToArtifact: (org.apache.maven.model.Dependency) -> org.eclipse.aether.graph.Dependency = + { + org.eclipse.aether.graph.Dependency( + DefaultArtifact( + it.groupId, + it.artifactId, + it.classifier, + null, + it.version + ), + it.scope, + ) + } + + val deps: List = + res.effectiveModel.dependencies?.map(dependencyToArtifact)?.toList() ?: emptyList() + + val managedDeps: List = + res.effectiveModel.dependencyManagement?.dependencies?.map(dependencyToArtifact) + ?.toList() ?: emptyList() + + val collectRequest = + CollectRequest(deps, managedDeps, embedderFacadeResponse.remoteRepositories) + + return try { + val collectResult = embedderFacadeResponse.repositorySystem!!.collectDependencies( + embedderFacadeResponse.session, + collectRequest + ) + + val returnList: MutableList = mutableListOf() + + collectResult.root.accept(object : DependencyVisitor { + override fun visitEnter(node: DependencyNode?): Boolean { + node?.dependency?.apply { + returnList.add( + Dependency( + groupId = this.artifact.groupId, + artifactId = this.artifact.artifactId, + version = this.artifact.version, + classifier = this.artifact.classifier, + packaging = this.artifact.extension, + scope = this.scope, + ) + ) + } + + return true + } + + override fun visitLeave(node: DependencyNode?): Boolean { + return true + } + }) + + this.result = returnList.toList() + + return true + } catch (e: DependencyCollectionException) { + LOGGER.warn("while resolving: ", e) + + return false + } + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryType.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryType.kt new file mode 100644 index 000000000..14d22cbd6 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/QueryType.kt @@ -0,0 +1,7 @@ +package io.github.pixee.maven.operator + +enum class QueryType { + NONE, + SAFE, + UNSAFE, +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleDependencyManagement.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleDependencyManagement.kt new file mode 100644 index 000000000..48b6366fa --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleDependencyManagement.kt @@ -0,0 +1,13 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.buildLookupExpressionForDependencyManagement + + +val SimpleDependencyManagement = object : AbstractCommand() { + override fun execute(pm: ProjectModel): Boolean { + val lookupExpression = + buildLookupExpressionForDependencyManagement(pm.dependency!!) + + return handleDependency(pm, lookupExpression) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleInsert.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleInsert.kt new file mode 100644 index 000000000..03c11d31f --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleInsert.kt @@ -0,0 +1,77 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.addIndentedElement +import io.github.pixee.maven.operator.Util.selectXPathNodes +import io.github.pixee.maven.operator.Util.upgradeVersionNode +import org.dom4j.Element + +/** + * Represents a POM Upgrade Strategy by simply adding a dependency/ section (and optionally a dependencyManagement/ section as well) + */ +val SimpleInsert = object : Command { + override fun execute(pm: ProjectModel): Boolean { + val dependencyManagementNodeList = + pm.pomFile.resultPom.selectXPathNodes("/m:project/m:dependencyManagement") + + val dependenciesNode = if (dependencyManagementNodeList.isEmpty()) { + val newDependencyManagementNode = + pm.pomFile.resultPom.rootElement.addIndentedElement( + pm.pomFile, + "dependencyManagement" + ) + + val dependencyManagementNode = + newDependencyManagementNode.addIndentedElement(pm.pomFile, "dependencies") + + dependencyManagementNode + } else { + (dependencyManagementNodeList.first() as Element).element("dependencies") + } + + val dependencyNode = appendCoordinates(dependenciesNode, pm) + + val versionNode = dependencyNode.addIndentedElement(pm.pomFile, "version") + + upgradeVersionNode(pm, versionNode, pm.pomFile) + + val dependenciesNodeList = + pm.pomFile.resultPom.selectXPathNodes("//m:project/m:dependencies") + + val rootDependencyNode: Element = if (dependenciesNodeList.isEmpty()) { + pm.pomFile.resultPom.rootElement.addIndentedElement(pm.pomFile, "dependencies") + } else if (dependenciesNodeList.size == 1) { + dependenciesNodeList[0] as Element + } else { + throw IllegalStateException("More than one dependencies node") + } + + appendCoordinates(rootDependencyNode, pm) + + return true + } + + /** + * Creates the XML Elements for a given dependency + */ + private fun appendCoordinates( + dependenciesNode: Element, + c: ProjectModel + ): Element { + val dependencyNode = dependenciesNode.addIndentedElement(c.pomFile, "dependency") + + val groupIdNode = dependencyNode.addIndentedElement(c.pomFile, "groupId") + + val dep = c.dependency!! + + groupIdNode.text = dep.groupId + + val artifactIdNode = dependencyNode.addIndentedElement(c.pomFile, "artifactId") + + artifactIdNode.text = dep.artifactId + + return dependencyNode + } + + override fun postProcess(c: ProjectModel): Boolean = false +} + diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleUpgrade.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleUpgrade.kt new file mode 100644 index 000000000..7e268befa --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SimpleUpgrade.kt @@ -0,0 +1,16 @@ +package io.github.pixee.maven.operator + +import io.github.pixee.maven.operator.Util.buildLookupExpressionForDependency + + +/** + * Represents bumping an existing dependency/ + */ +val SimpleUpgrade = object : AbstractCommand() { + override fun execute(pm: ProjectModel): Boolean { + val lookupExpressionForDependency = + buildLookupExpressionForDependency(pm.dependency!!) + + return handleDependency(pm, lookupExpressionForDependency) + } +} \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SupportCommand.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SupportCommand.kt new file mode 100644 index 000000000..be9286d58 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/SupportCommand.kt @@ -0,0 +1,6 @@ +package io.github.pixee.maven.operator + +/** + * Tag Interface to the chain to allow it to figure out whether things were modified + */ +interface SupportCommand \ No newline at end of file diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Util.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Util.kt new file mode 100644 index 000000000..3094a2973 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/Util.kt @@ -0,0 +1,283 @@ +@file:Suppress("DEPRECATION") + +package io.github.pixee.maven.operator + +import com.github.zafarkhaja.semver.Version +import org.apache.commons.lang3.StringUtils +import org.apache.commons.lang3.SystemUtils +import org.apache.commons.lang3.text.StrSubstitutor +import org.dom4j.Element +import org.dom4j.Node +import org.dom4j.Text +import org.dom4j.tree.DefaultText +import org.jaxen.SimpleNamespaceContext +import org.jaxen.XPath +import org.jaxen.dom4j.Dom4jXPath +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File + + +/** + * Common Utilities + */ +object Util { + private val LOGGER: Logger = LoggerFactory.getLogger(Util::class.java) + + /** + * Extension Method that easily allows to add an element inside another while + * retaining formatting + * + * @param d ProjectModel / Context + * @param name new element ("tag") name + * @return created element inside `this` object, already indented after and (optionally) before + */ + fun Element.addIndentedElement(d: POMDocument, name: String): Element { + val contentList = this.content() + + val indentLevel = findIndentLevel(this) + + val prefix = d.endl + StringUtils.repeat(d.indent, 1 + indentLevel) + + val suffix = d.endl + StringUtils.repeat(d.indent, indentLevel) + + if (contentList.isNotEmpty() && contentList.last() is Text) { + val lastElement = contentList.last() as Text + + if (StringUtils.isWhitespace(lastElement.text)) { + contentList.remove(contentList.last()) + } + } + + contentList.add(DefaultText(prefix)) + + val newElement = this.addElement(name) + + contentList.add(DefaultText(suffix)) + + d.dirty = true + + return newElement + } + + /** + * Guesses the current indent level of the nearest nodes + * + * @return indent level + */ + private fun findIndentLevel(startingNode: Element): Int { + var level = 0 + + var node = startingNode + + while (node.parent != null) { + level += 1 + node = node.parent + } + + return level + } + + /** + * Represents a Property Reference - as a regex + */ + private val PROPERTY_REFERENCE_REGEX = Regex("^\\\$\\{(.*)}$") + + /** + * Upserts a given property + */ + private fun upgradeProperty(c: ProjectModel, d: POMDocument, propertyName: String) { + if (null == d.resultPom.rootElement.element("properties")) { + d.resultPom.rootElement.addIndentedElement(d, "properties") + } + + val parentPropertyElement = d.resultPom.rootElement.element("properties") + + if (null == parentPropertyElement.element(propertyName)) { + parentPropertyElement.addIndentedElement(d, propertyName) + } else { + if (!c.overrideIfAlreadyExists) { + val propertyReferenceRE = Regex.fromLiteral("\${$propertyName}") + + val numberOfAllCurrentMatches = + propertyReferenceRE.findAll(d.resultPom.asXML()).toList().size + + if (numberOfAllCurrentMatches > 1) { + throw IllegalStateException("Property $propertyName is already defined - and used more than once.") + } + } + } + + val propertyElement = parentPropertyElement.element(propertyName) + + if (!(propertyElement.text ?: "").trim().equals(c.dependency!!.version)) { + propertyElement.text = c.dependency!!.version + + d.dirty = true + } + } + + /** + * Creates a property Name + */ + internal fun propertyName(c: ProjectModel, versionNode: Element): String { + val version = versionNode.textTrim + + if (PROPERTY_REFERENCE_REGEX.matches(version)) { + val match = PROPERTY_REFERENCE_REGEX.find(version) + + val firstMatch = match!!.groups[1]!! + + return firstMatch.value + } + + return "versions." + c.dependency!!.artifactId + } + + /** + * Identifies if an upgrade is needed + */ + internal fun findOutIfUpgradeIsNeeded(c: ProjectModel, versionNode: Element): Boolean { + val currentVersionNodeText = resolveVersion(c, versionNode.text!!) + + val currentVersion = Version.valueOf(currentVersionNodeText) + val newVersion = Version.valueOf(c.dependency!!.version) + + @Suppress("UnnecessaryVariable") val versionsAreIncreasing = + newVersion.greaterThan(currentVersion) + + return versionsAreIncreasing + } + + private fun resolveVersion(c: ProjectModel, versionText: String): String = + if (PROPERTY_REFERENCE_REGEX.matches(versionText)) { + @Suppress("DEPRECATION") + StrSubstitutor(c.resolvedProperties).replace(versionText) + } else { + versionText + } + + /** + * Escapes a Property Name + */ + private fun escapedPropertyName(propertyName: String): String = + "\${$propertyName}" + + /** + * Given a Version Node, upgrades a resulting POM + */ + internal fun upgradeVersionNode( + c: ProjectModel, + versionNode: Element, + pomDocumentHoldingProperty: POMDocument + ) { + if (c.useProperties) { + val propertyName = propertyName(c, versionNode) + + // define property + upgradeProperty(c, pomDocumentHoldingProperty, propertyName) + + versionNode.text = escapedPropertyName(propertyName) + } else { + if (!(versionNode.text ?: "").trim().equals(c.dependency!!.version)) { + pomDocumentHoldingProperty.dirty = true + versionNode.text = c.dependency!!.version + } + } + } + + /** + * Builds a Lookup Expression String for a given dependency + * + * @param dependency Dependency + */ + fun buildLookupExpressionForDependency(dependency: Dependency): String = + "/m:project" + + "/m:dependencies" + + "/m:dependency" + + /* */ "[./m:groupId[text()='${dependency.groupId}'] and " + + /* */ "./m:artifactId[text()='${dependency.artifactId}']" + + "]" + + /** + * Builds a Lookup Expression String for a given dependency, but under the >dependencyManagement> section + * + * @param dependency Dependency + */ + fun buildLookupExpressionForDependencyManagement(dependency: Dependency): String = + "/m:project" + + "/m:dependencyManagement" + + "/m:dependencies" + + "/m:dependency" + + /* */ "[./m:groupId[text()='${dependency.groupId}'] and " + + /* */ "./m:artifactId[text()='${dependency.artifactId}']" + + "]" + + /** + * Extension Function to Select the XPath Nodes + * + * @param expression expression to use + */ + @Suppress("UNCHECKED_CAST") + fun Node.selectXPathNodes(expression: String) = + createXPathExpression(expression).selectNodes(this)!! as List + + /** + * Creates a XPath Expression from a given expression string + * + * @param expression expression to create xpath from + */ + private fun createXPathExpression(expression: String): XPath { + val xpath = Dom4jXPath(expression) + + xpath.namespaceContext = namespaceContext + + return xpath + } + + /** + * Hard-Coded POM Namespace Map + */ + private val namespaceContext = SimpleNamespaceContext( + mapOf( + "m" to "http://maven.apache.org/POM/4.0.0" + ) + ) + + + internal fun which(path: String): File? { + val nativeExecutables: List = if (SystemUtils.IS_OS_WINDOWS) { + listOf("", ".exe", ".bat", ".cmd").map { path + it }.toList() + } else { + listOf(path) + } + + val pathContentString = System.getenv("PATH") + + val pathElements = pathContentString.split(File.pathSeparatorChar) + + val possiblePaths = nativeExecutables.flatMap { executable -> + pathElements.map { pathElement -> + File(File(pathElement), executable) + } + } + + val isCliCallable: (File) -> Boolean = if (SystemUtils.IS_OS_WINDOWS) { it -> + it.exists() && it.isFile + } else { it -> + it.exists() && it.isFile && it.canExecute() + } + + val result = possiblePaths.findLast(isCliCallable) + + if (null == result) { + LOGGER.warn( + "Unable to find mvn executable (execs: {}, path: {})", + nativeExecutables.joinToString("/"), + pathContentString + ) + } + + return result + } +} diff --git a/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/WrongDependencyTypeException.kt b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/WrongDependencyTypeException.kt new file mode 100644 index 000000000..9c13bbe89 --- /dev/null +++ b/pom-operator/src/main/kotlin/io/github/pixee/maven/operator/WrongDependencyTypeException.kt @@ -0,0 +1,3 @@ +package io.github.pixee.maven.operator + +class WrongDependencyTypeException(message: String) : RuntimeException(message) diff --git a/pom-operator/src/test/java/io/github/pixee/maven/operator/test/POMOperatorJavaIT.java b/pom-operator/src/test/java/io/github/pixee/maven/operator/test/POMOperatorJavaIT.java new file mode 100644 index 000000000..582f21ee4 --- /dev/null +++ b/pom-operator/src/test/java/io/github/pixee/maven/operator/test/POMOperatorJavaIT.java @@ -0,0 +1,40 @@ +package io.github.pixee.maven.operator.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.github.pixee.maven.operator.Util; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang3.SystemUtils; +import org.junit.jupiter.api.Test; + +public class POMOperatorJavaIT { + @Test + public void testJavaSample() throws Exception { + String mvnAbsPath = Util.INSTANCE.which$pom_operator("mvn").getAbsolutePath(); + + List argList = + Arrays.asList(mvnAbsPath, "-B", "-N", "-f", "java-sample/pom.xml", "verify"); + + if (SystemUtils.IS_OS_WINDOWS) { + List newArgList = + Arrays.asList(Util.INSTANCE.which$pom_operator("cmd").getAbsolutePath(), "/c"); + + newArgList.addAll(argList); + + argList = newArgList; + } + + String[] args = argList.toArray(new String[0]); + + ProcessBuilder psBuilder = new ProcessBuilder(args).inheritIO(); + + psBuilder.environment().putAll(System.getenv()); + + Process process = psBuilder.start(); + + int retCode = process.waitFor(); + + assertEquals(0, retCode, "Embedded execution must return zero"); + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/AbstractTestBase.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/AbstractTestBase.kt new file mode 100644 index 000000000..9424d3e6b --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/AbstractTestBase.kt @@ -0,0 +1,110 @@ +package io.github.pixee.maven.operator.test + +import `fun`.mike.dmp.DiffMatchPatch +import io.github.pixee.maven.operator.POMOperator +import io.github.pixee.maven.operator.ProjectModel +import io.github.pixee.maven.operator.ProjectModelFactory +import org.dom4j.Document +import org.dom4j.io.SAXReader +import org.junit.jupiter.api.Assertions.assertFalse +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.xmlunit.builder.DiffBuilder +import org.xmlunit.builder.Input +import org.xmlunit.diff.Diff +import java.io.File +import java.net.URLDecoder + +open class AbstractTestBase { + protected val LOGGER: Logger = LoggerFactory.getLogger(POMOperatorTest::class.java) + + fun getResource(name: String) = this.javaClass.getResource(name)!! + + fun getResourceAsFile(name: String): File = File(getResource(name).toURI()) + + /** + * Implements a Given-When-Then idiom + * + * @param g: Given - returns a context + * @param t: Then - validates given a context/ProjectModel + */ + protected fun gwt(g: () -> ProjectModel, t: (p: ProjectModel) -> Unit) { + val context = g() + + LOGGER.debug("context: {}", context) + + POMOperator.modify(context) + + LOGGER.debug("context after: {}", context) + + t(context) + } + + protected fun gwt(name: String, pmf: ProjectModelFactory): ProjectModel = + gwt(name, pmf.build()) + + protected fun gwt(testName: String, context: ProjectModel): ProjectModel { + val resultFile = "pom-$testName-result.xml" + val resource = this.javaClass.javaClass.getResource(resultFile) + + if (resource != null) { + val outcome = SAXReader().read(resource) + + LOGGER.debug("context: {}", context) + + POMOperator.modify(context) + + LOGGER.debug("context after: {}", context) + + assertFalse( + getXmlDifferences(context.pomFile.resultPom, outcome).hasDifferences(), + "Expected and outcome have differences" + ) + } else { + val resultFilePath = "src/test/resources/" + this.javaClass.`package`.name.replace( + ".", + "/" + ) + "/" + resultFile + + LOGGER.debug("context: {}", context) + + POMOperator.modify(context) + + LOGGER.debug("context after: {}", context) + + LOGGER.warn("File $resultFilePath not found - writing results instead and ignorning assertions at all") + + File(resultFilePath).writeBytes(context.pomFile.resultPomBytes) + } + + return context + } + + protected fun getXmlDifferences( + original: Document, + modified: Document + ): Diff { + val originalDoc = Input.fromString(original.asXML()).build() + val modifiedDoc = Input.fromString(modified.asXML()).build() + + val diff = DiffBuilder.compare(originalDoc).withTest(modifiedDoc).ignoreWhitespace() + .checkForSimilar().build() + + LOGGER.debug("diff: {}", diff) + + return diff + } + + protected fun getTextDifferences(pomDocument: Document, resultPom: Document): Any { + val pomDocumentAsString = pomDocument.asXML() + val resultPomAsString = resultPom.asXML() + + val dmp = DiffMatchPatch() + + val diffs = dmp.patch_make(pomDocumentAsString, resultPomAsString) + + val patch = dmp.patch_toText(diffs) + + return URLDecoder.decode(patch, "utf-8") + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/MassRepoTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/MassRepoTest.kt new file mode 100644 index 000000000..a0d42ccac --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/MassRepoTest.kt @@ -0,0 +1,321 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.Dependency +import io.github.pixee.maven.operator.POMOperator +import io.github.pixee.maven.operator.POMScanner +import io.github.pixee.maven.operator.ProjectModelFactory +import io.github.pixee.maven.operator.Util.which +import org.apache.commons.lang3.SystemUtils +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File +import java.lang.AssertionError +import java.lang.Exception + +data class TestRepo( + val slug: String, + val branch: String = "master", + val pomPath: String = "pom.xml", + val useProperties: Boolean = false, + val useScanner: Boolean = false, + val offline: Boolean = false, + val commitId: String? = null, +) { + fun cacheDir() = BASE_CACHE_DIR.resolve("repo-%08X".format(slug.hashCode())) + + companion object { + val BASE_CACHE_DIR: File = File(System.getProperty("user.dir") + "/.cache").absoluteFile + } +} + +/** + * This test actually picks up several github repos and apply the POM Operator and checks for a runtime dependency change + */ +class MassRepoIT { + /* + https://github.com/trending/java?since=daily + + apache/pulsar + metersphere/metersphere + apache/rocketmq + OpenAPITools/openapi-generator + casbin/jcasbin + trinodb/trino + bytedeco/javacv + flowable/flowable-engine + + Azure/azure-sdk-for-java + questdb/questdb + */ + + private val repos = listOf( + TestRepo( + "WebGoat/WebGoat", + useProperties = true, + branch = "main", + useScanner = false, + commitId = "e75cfbeb110e3d3a2ca3c8fee2754992d89c419d", + pomPath = "webgoat-lessons/xxe/pom.xml", + ) to "io.github.pixee:java-security-toolkit:1.0.2", + TestRepo( + "WebGoat/WebGoat", + useProperties = true, + branch = "main", + useScanner = true, + offline = true, + pomPath = "webgoat-container/pom.xml" + ) to "io.github.pixee:java-security-toolkit:1.0.2", + TestRepo( + "WebGoat/WebGoat", + useProperties = true, + branch = "main", + useScanner = false, + pomPath = "webgoat-container/pom.xml" + ) to "io.github.pixee:java-security-toolkit:1.0.2", + TestRepo( + "WebGoat/WebGoat", + useProperties = true, + branch = "main", + pomPath = "webgoat-container/pom.xml" + ) to "io.github.pixee:java-security-toolkit:1.0.2", + TestRepo( + "CRRogo/vert.x", + useProperties = true, + ) to "io.github.pixee:java-security-toolkit:1.0.2", + TestRepo( + "apache/pulsar", + pomPath = "pulsar-broker/pom.xml" + ) to "commons-codec:commons-codec:1.14", + TestRepo( + "apache/rocketmq", + pomPath = "common/pom.xml" + ) to "commons-codec:commons-codec:1.15", + TestRepo( + "OpenAPITools/openapi-generator", + pomPath = "modules/openapi-generator-core/pom.xml" + ) to "com.google.guava:guava:31.0-jre", + TestRepo( + "casbin/jcasbin", + ) to "com.google.code.gson:gson:2.8.0", + TestRepo( + "bytedeco/javacv" + ) to "org.jogamp.jocl:jocl-main:2.3.1", + ) + + /** + * Checks out - or resets - a stored github repo + */ + private fun checkoutOrResetCachedRepo(repo: TestRepo) { + LOGGER.info("Checkout out $repo into ${repo.cacheDir()}") + + if (!repo.cacheDir().exists()) { + // git clone -b branch github.com/slug/ dir + val command = arrayOf( + "git", + "clone", + "-b", + repo.branch, + "https://github.com/${repo.slug}.git", + repo.cacheDir().canonicalPath + ) + + LOGGER.debug("Running command: ${command.joinToString(" ")}") + + val process = ProcessBuilder(*command) + .directory(TestRepo.BASE_CACHE_DIR.canonicalFile) + .inheritIO() + .start() + + process.waitFor() + } else { + val command = arrayOf("git", "reset", "--hard", "HEAD") + + LOGGER.debug("Running command: ${command.joinToString(" ")}") + + val process = ProcessBuilder(*command) + .directory(repo.cacheDir()) + .inheritIO() + .start() + + process.waitFor() + } + + if (null != repo.commitId) { + val command = arrayOf("git", "checkout", repo.commitId) + + LOGGER.debug("Running command: ${command.joinToString(" ")}") + + val process = ProcessBuilder(*command) + .directory(repo.cacheDir()) + .inheritIO() + .start() + + process.waitFor() + } + } + + /** + * Sanity Test on a single repo + */ + @Test + fun testBasic() { + val firstCase = repos.first() + + testOnRepo(firstCase.first, firstCase.second) + } + + /** + * THATS THE FULL TEST + */ + @Test + fun testAllOthers() { + repos.forEachIndexed { n, pair -> + try { + testOnRepo(pair.first, pair.second) + } catch (e: Throwable) { + throw AssertionError("while trying example $n of $pair", e) + } + } + } + + private fun testOnRepo( + sampleRepo: TestRepo, + dependencyToUpgradeString: String + ) { + LOGGER.info( + "Testing on repo {}, branch {} with dependency {} ({})", + sampleRepo.slug, + sampleRepo.branch, + dependencyToUpgradeString, + sampleRepo, + ) + + checkoutOrResetCachedRepo(sampleRepo) + + val originalDependencies = getDependenciesFrom(sampleRepo) + + LOGGER.info("dependencies: {}", originalDependencies) + + val dependencyToUpgrade = Dependency.fromString(dependencyToUpgradeString) + + val projectModelFactory = if (sampleRepo.useScanner) { + POMScanner.scanFrom( + File(sampleRepo.cacheDir(), sampleRepo.pomPath), + sampleRepo.cacheDir() + ) + } else { + ProjectModelFactory.Companion.load(File(sampleRepo.cacheDir(), sampleRepo.pomPath)) + } + + val context = projectModelFactory + .withDependency(dependencyToUpgrade) + .withSkipIfNewer(false) + .withUseProperties(sampleRepo.useProperties) + .withOffline(sampleRepo.offline) + .build() + + val result = POMOperator.modify(context) + + context.allPomFiles.filter { it.dirty }.forEach { + it.file.writeBytes(it.resultPomBytes) + } + + val finalDependencies = + getDependenciesFrom(sampleRepo) + + LOGGER.info("dependencies: {}", finalDependencies) + + val queryFailed = "" == originalDependencies && "" == finalDependencies + + if (queryFailed) { + assertTrue(result, "Must be modified even when query failed") + } else { + val dependencyAsStringWithPackaging = dependencyToUpgrade.toString() + + assertFalse( + originalDependencies.contains( + dependencyAsStringWithPackaging + ), + "Dependency should be originally missing" + ) + assertTrue( + finalDependencies.contains( + dependencyAsStringWithPackaging + ), + "New Dependency should be appearing" + ) + } + } + + private fun getDependenciesFrom(repo: TestRepo): String = try { + getDependenciesFrom(repo.pomPath, repo.cacheDir()) + } catch (e: Exception) { + val pomFile = File(repo.cacheDir(), repo.pomPath) + + val dependencies = + POMOperator.queryDependency( + POMScanner.scanFrom(pomFile, repo.cacheDir()) + .withRepositoryPath(repo.cacheDir()) + .withOffline(false) + .build() + ) + + dependencies.joinToString("\n") + } + + private fun getDependenciesFrom(pomPath: String, dir: File): String { + val outputFile = File.createTempFile("tmp-pom", ".txt") + + if (outputFile.exists()) { + outputFile.delete() + } + + val command = if (SystemUtils.IS_OS_WINDOWS) { + listOf(which("cmd")!!.canonicalPath, "/c") + } else { + emptyList() + } + listOf( + which("mvn")!!.canonicalPath, + "-B", + "-f", + pomPath, + "dependency:tree", + "-Dscope=runtime", + "-DoutputFile=${outputFile.canonicalPath}" + ) + + LOGGER.debug("Running: " + command.joinToString(" ")) + LOGGER.debug("Dir: " + dir) + + val process = ProcessBuilder(*command.toTypedArray()) + .directory(dir) + .inheritIO() + .start() + + process.waitFor() + + if (! outputFile.exists()) + return "" + + val result = outputFile.readText() + + outputFile.delete() + + return result + } + + companion object { + val LOGGER: Logger = LoggerFactory.getLogger(MassRepoIT::class.java) + } + + init { + /** + * Creates the Cache Directory + */ + if (!TestRepo.BASE_CACHE_DIR.exists()) + TestRepo.BASE_CACHE_DIR.mkdirs() + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorMultipomTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorMultipomTest.kt new file mode 100644 index 000000000..e05398cf0 --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorMultipomTest.kt @@ -0,0 +1,160 @@ +package io.github.pixee.maven.operator.test + +import com.google.common.io.Files +import io.github.pixee.maven.operator.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.io.File +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class POMOperatorMultipomTest : AbstractTestBase() { + @Test + fun testWithParentAndChildMissingPackaging() { + val parentResource = getResource("parent-and-child-parent-broken.xml") + + val parentPomFiles = listOf(POMDocumentFactory.load(parentResource)) + + val parentPom = ProjectModelFactory.load( + parentResource, + ).withParentPomFiles(parentPomFiles) + + assertThrows { + gwt( + "parent-and-child", + parentPom.withDependency(Dependency.fromString("org.dom4j:dom4j:2.0.3")) + ) + } + } + + @Test + fun testWithParentAndChildWrongType() { + val parentResource = getResource("parent-and-child-child-broken.xml") + + val parentPomFile = POMDocumentFactory.load(getResource("parent-and-child-parent.xml")) + + val parentPomFiles = listOf(parentPomFile) + + val parentPom = ProjectModelFactory.load( + parentResource, + ).withParentPomFiles(parentPomFiles) + + assertThrows { + gwt( + "parent-and-child-wrong-type", + parentPom.withDependency(Dependency.fromString("org.dom4j:dom4j:2.0.3")) + ) + } + } + + @Test + fun testWithMultiplePomsBasicNoVersionProperty() { + val parentPomFile = getResource("sample-parent/pom.xml") + + val projectModelFactory = ProjectModelFactory + .load( + getResource("sample-child-with-relativepath.xml") + ) + .withParentPomFiles(listOf(POMDocumentFactory.load(parentPomFile))) + .withUseProperties(false) + + val result = gwt( + "multiple-pom-basic-no-version-property", + projectModelFactory.withDependency(Dependency.fromString("org.dom4j:dom4j:2.0.3")) + ) + + validateDepsFrom(result) + + assertTrue(result.allPomFiles.size == 2, "There should be two files") + assertTrue(result.allPomFiles.all { it.dirty }, "All files were modified") + } + + @Test + fun testWithMultiplePomsBasicWithVersionProperty() { + val parentPomFile = getResource("sample-parent/pom.xml") + + val sampleChild = getResource("sample-child-with-relativepath.xml") + + val parentPom = ProjectModelFactory.load( + sampleChild + ).withParentPomFiles(listOf(POMDocumentFactory.load(parentPomFile))) + .withUseProperties(true) + + val result = gwt( + "multiple-pom-basic-with-version-property", + parentPom.withDependency(Dependency.fromString("org.dom4j:dom4j:2.0.3")) + ) + + validateDepsFrom(result) + + assertTrue(result.allPomFiles.size == 2, "There should be two files") + assertTrue(result.allPomFiles.all { it.dirty }, "All files were modified") + + val parentPomString = String(result.parentPomFiles.first().resultPomBytes, Charsets.UTF_8) + val pomString = String(result.pomFile.resultPomBytes, Charsets.UTF_8) + + val version = result.dependency!!.version!! + + assertTrue( + parentPomString.contains("versions.dom4j>${version}"), + "Must contain property with version set on parent pom" + ) + assertFalse(pomString.contains(version), "Must not have reference to version on pom") + } + + fun validateDepsFrom(context: ProjectModel) { + val resultFiles = copyFiles(context) + + resultFiles.entries.forEach { + System.err.println("from ${it.key.pomPath} -> ${it.value}") + } + + val pomFile = resultFiles.entries.first().value + + val dependencies = POMOperator.queryDependency( + ProjectModelFactory.load(pomFile) + .withQueryType(QueryType.UNSAFE) + .build() + ) + + val foundDependency = dependencies.contains(context.dependency!!) + + assertTrue( + foundDependency, + "Dependency ${context.dependency!!} must be present in context $context ($dependencies)" + ) + } + + fun copyFiles(context: ProjectModel): Map { + var commonPath = File(context.pomFile.pomPath!!.toURI()).canonicalFile + + for (p in context.parentPomFiles) { + commonPath = File( + File(p.pomPath!!.toURI()).canonicalPath.toString() + .commonPrefixWith(commonPath.canonicalPath) + ) + } + + val commonPathLen = commonPath.parent.length + + val tmpOutputDir = Files.createTempDir() + + val result = context.allPomFiles.map { p -> + val pAsFile = File(p.pomPath!!.toURI()) + + val relPath = pAsFile.canonicalPath.substring(1 + commonPathLen) + + val targetPath = File(tmpOutputDir, relPath) + + if (!targetPath.parentFile.exists()) { + targetPath.parentFile.mkdirs() + } + + targetPath.writeBytes(p.resultPomBytes) + + p to targetPath + }.toMap() + + return result + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorQueryTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorQueryTest.kt new file mode 100644 index 000000000..cb079973d --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorQueryTest.kt @@ -0,0 +1,442 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.* +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.File +import java.lang.IllegalStateException +import java.nio.file.Files + +class POMOperatorQueryTest { + companion object { + private val LOGGER: Logger = LoggerFactory.getLogger(POMOperatorTest::class.java) + } + + @Test + fun testBasicQuery() { + QueryType.values().filterNot { it == QueryType.NONE }.forEach { queryType -> + val context = + ProjectModelFactory + .load(this.javaClass.getResource("pom-1.xml")!!) + .withQueryType(queryType) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + } + } + + @Test + fun testFailedSafeQuery() { + val context = + ProjectModelFactory + .load(this.javaClass.getResource("pom-broken.xml")!!) + .withQueryType(QueryType.SAFE) + .build() + + val dependencies = POMOperator.queryDependency(context) + + assertTrue(dependencies.isEmpty(), "Dependencies are empty") + } + + @Test + fun testFailedUnsafeQuery() { + val context = + ProjectModelFactory + .load(this.javaClass.getResource("pom-broken.xml")!!) + .withQueryType(QueryType.UNSAFE) + .build() + + assertThrows { POMOperator.queryDependency(context) } + } + + @Test + fun testAllQueryTypes() { + listOf("pom-1.xml", "pom-3.xml").forEach { pomFile -> + Chain.AVAILABLE_QUERY_COMMANDS.forEach { + val commandClassName = "io.github.pixee.maven.operator.${it.second}" + + val commandListOverride = + listOf(Class.forName(commandClassName).newInstance() as Command) + + val context = + ProjectModelFactory + .load(this.javaClass.getResource(pomFile)!!) + .withQueryType(QueryType.UNSAFE) + .build() + + val dependencies = + POMOperator.queryDependency(context, commandList = commandListOverride) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + } + } + } + + + @Test + fun testTemporaryDirectory() { + QueryType.values().filterNot { it == QueryType.NONE }.forEach { queryType -> + val tempDirectory = File("/tmp/mvn-repo-" + System.currentTimeMillis() + ".dir") + + LOGGER.info("Using queryType: $queryType at $tempDirectory") + + assertFalse(tempDirectory.exists(), "Temp Directory does not exist initially") + assertEquals( + tempDirectory.list()?.filter { File(it).isDirectory }?.size ?: 0, + 0, + "There must be no files" + ) + + val context = + ProjectModelFactory + .load(this.javaClass.getResource("pom-1.xml")!!) + .withQueryType(queryType) + .withRepositoryPath(tempDirectory) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + + assertTrue(tempDirectory.exists(), "Temp Directory ends up existing") + assertTrue(tempDirectory.isDirectory, "Temp Directory is a directory") + } + } + + @Test + fun testTemporaryDirectoryAndFullyOffline() { + QueryType.values().filterNot { it == QueryType.NONE }.filter { it == QueryType.SAFE } + .forEach { queryType -> + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val context = + ProjectModelFactory + .load(this.javaClass.getResource("pom-1.xml")!!) + .withQueryType(queryType) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + } + } + + @Test + fun testOnSyntheticDependency() { + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val tempPom = File(tempDirectory, "pom.xml").toPath() + + val randomName = "random-artifact-" + System.currentTimeMillis() + + Files.write( + tempPom, """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + + dummyorg + ${randomName} + 2.1.3 + + + + + """.trimIndent().toByteArray() + ) + + val context = + ProjectModelFactory + .load(tempPom.toFile()) + .withQueryType(QueryType.SAFE) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + + assertTrue(dependencies.first().artifactId.equals(randomName), "Random name matches") + } + + @Test + fun testOnCompositeSyntheticDependency() { + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val tempParentPom = File(tempDirectory, "pom-parent.xml").toPath() + val tempPom = File(tempDirectory, "pom.xml").toPath() + + val randomName = "random-artifact-" + System.currentTimeMillis() + + Files.write( + tempParentPom, """ + + 4.0.0 + + somethingelse + br.com.ingenieux + 1 + + pom + + + + + dummyorg + managed-${randomName} + 1 + + + + + """.trim().toByteArray() + ) + + Files.write( + tempPom, """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + somethingelse + br.com.ingenieux + 1 + ./pom-parent.xml + + + + + dummyorg + ${randomName} + 2.1.3 + + + dummyorg + managed-${randomName} + + + + + """.trim().toByteArray() + ) + + val context = + ProjectModelFactory + .load(tempPom.toFile()) + .withQueryType(QueryType.SAFE) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are not empty") + + assertTrue(dependencies.first().artifactId.equals(randomName), "Random name matches") + } + + @Test + fun testOnCompositeSyntheticDependencyIncompleteWithoutParsing() { + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val tempPom = File(tempDirectory, "pom.xml").toPath() + + val randomName = "random-artifact-" + System.currentTimeMillis() + + Files.write( + tempPom, """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + somethingelse + br.com.ingenieux + 1 + ./pom-parent.xml + + + + + dummyorg + ${randomName} + 2.1.3 + + + dummyorg + managed-${randomName} + + + + + """.trim().toByteArray() + ) + + val context = + ProjectModelFactory + .load(tempPom.toFile()) + .withQueryType(QueryType.SAFE) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = POMOperator.queryDependency( + context, + commandList = getCommandListFor("QueryByEmbedder", "QueryByResolver") + ) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isEmpty(), "Dependencies are empty") + } + + @Test + fun testOnCompositeSyntheticDependencyIncompleteButWithParser() { + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val tempPom = File(tempDirectory, "pom.xml").toPath() + + val randomName = "random-artifact-" + System.currentTimeMillis() + + Files.write( + tempPom, """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + somethingelse + br.com.ingenieux + 1 + ./pom-parent.xml + + + + + + dummyorg + managed-${randomName} + 0.0.1 + + + + + + + dummyorg + ${randomName} + 2.1.3 + + + dummyorg + managed-${randomName} + + + + + """.trim().toByteArray() + ) + + val context = + ProjectModelFactory + .load(tempPom.toFile()) + .withQueryType(QueryType.SAFE) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = + POMOperator.queryDependency(context, commandList = getCommandListFor("QueryByParsing")) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isNotEmpty(), "Dependencies are empty") + + val foundDependencies = + dependencies.firstOrNull { it.version == "0.0.1" && it.artifactId.equals("managed-$randomName") } + + assertTrue(foundDependencies != null, "there's a dependencyManaged-version") + } + + private fun getCommandListFor(vararg names: String): List = + names.map { + val commandClassName = "io.github.pixee.maven.operator.${it}" + + val commandInstance = + Class.forName(commandClassName).newInstance() as Command + + commandInstance + }.toList() + + @Test + fun testOfflineQueryResolution() { + val tempDirectory = Files.createTempDirectory("mvn-repo").toFile() + + val context = + ProjectModelFactory + .load(javaClass.getResource("nested/child/pom/pom-3-child.xml")) + .withQueryType(QueryType.SAFE) + .withRepositoryPath(tempDirectory) + .withOffline(true) + .build() + + val dependencies = POMOperator.queryDependency(context) + + LOGGER.debug("Dependencies found: {}", dependencies) + + assertTrue(dependencies.isEmpty(), "Dependencies are empty") + + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorTest.kt new file mode 100644 index 000000000..a7ab08234 --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMOperatorTest.kt @@ -0,0 +1,456 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.* +import io.github.pixee.maven.operator.Util.buildLookupExpressionForDependency +import io.github.pixee.maven.operator.Util.selectXPathNodes +import io.github.pixee.maven.operator.Util.which +import org.apache.commons.lang3.SystemUtils +import org.dom4j.DocumentException +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.xmlunit.diff.ComparisonType +import java.io.File +import java.nio.charset.Charset +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Unit test for simple App. + */ +class POMOperatorTest : AbstractTestBase() { + @Test + fun testWithBrokenPom() { + assertThrows { + gwt( + "broken-pom", + ProjectModelFactory.load( + POMOperatorTest::class.java.getResource("broken-pom.xml")!!, + ).withDependency(Dependency.fromString("org.dom4j:dom4j:2.0.3")) + ) + } + } + + @Test + fun testWithMultipleDependencies() { + val deps = listOf( + "org.slf4j:slf4j-api:1.7.25", + "io.github.pixee:java-code-security-toolkit:1.0.2", + "org.owasp.encoder:encoder:1.2.3", + ).map { Dependency.fromString(it) }.toList() + + val testPom = File.createTempFile("pom", ".xml") + + POMOperatorTest::class.java.getResourceAsStream("sample-bad-pom.xml")!! + .copyTo(testPom.outputStream()) + + deps.forEach { d -> + val projectModel = ProjectModelFactory.load(testPom) + .withDependency(d) + .withUseProperties(true) + .withOverrideIfAlreadyExists(true) + .build() + + if (POMOperator.modify(projectModel)) { + assertTrue(projectModel.pomFile.dirty, "Original POM File is Dirty") + + val resultPomAsXml = String(projectModel.pomFile.resultPomBytes) + + LOGGER.debug("resultPomAsXml: {}", resultPomAsXml) + + testPom.writeBytes(projectModel.pomFile.resultPomBytes) + } else { + throw IllegalStateException("Code that shouldn't be reached out at all") + } + } + + val resolvedDeps = POMOperator.queryDependency( + ProjectModelFactory.load(testPom).withQueryType(QueryType.SAFE).build() + ) + + val testPomContents = testPom.readText() + + assertTrue(3 == resolvedDeps.size, "Must have three dependencies") + assertTrue(testPomContents.contains(""), + "There must be an empty element with three spaces inside a comment" + ) + } + + @Test + fun testCaseWithProperty() { + val dependencyToUpgrade = + Dependency("org.dom4j", "dom4j", version = "1.0.0") + + val context = gwt( + "case-with-property", + ProjectModelFactory.load( + POMOperatorTest::class.java.getResource("pom-with-property-simple.xml")!!, + ).withDependency(dependencyToUpgrade).withUseProperties(true).withSkipIfNewer(true) + ) + + LOGGER.debug("original pom: {}", context.pomFile.pomDocument.asXML()) + LOGGER.debug("resulting pom: {}", context.pomFile.resultPom.asXML()) + + assertTrue(context.pomFile.dirty, "Original POM File is Dirty") + + val diff = getXmlDifferences(context.pomFile.pomDocument, context.pomFile.resultPom) + + assertThat("Document has differences", diff.hasDifferences()) + + val differenceList = diff.differences.toList() + + assertThat("Document has one difference", 1 == differenceList.size) + + assertThat( + "Document changes a single version", + differenceList.first().toString() + .startsWith("Expected text value '0.0.1-SNAPSHOT' but was '1.0.0'") + ) + + assertEquals( + differenceList.first().comparison.testDetails.xPath, + "/project[1]/properties[1]/sample.version[1]/text()[1]", + "Document changes a property called 'sample.version'" + ) + } + + @Test + fun testCaseWithPropertyDefinedTwice() { + val dependencyToUpgrade = + Dependency("org.dom4j", "dom4j", version = "1.0.0") + + val originalPom = """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + 0.0.1 + + + + + org.dom4j + dom4j + ${'$'}{dom4j.version} + + + org.dom4j + dom4j-other + ${'$'}{dom4j.version} + + + + """.trimIndent() + val context = + ProjectModelFactory.load( + originalPom.byteInputStream(), + ).withDependency(dependencyToUpgrade).withUseProperties(true) + .withOverrideIfAlreadyExists(false) + .build() + + assertThrows { + POMOperator.modify(context) + } + } + + @Test + fun testCaseWithoutPropertyButDefiningOne() { + val dependencyToUpgrade = + Dependency("org.dom4j", "dom4j", version = "1.0.0") + + val originalPom = """ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + + org.dom4j + dom4j + 0.0.1-SNAPSHOT + + + + """.trim() + val context = + ProjectModelFactory.load( + originalPom.byteInputStream(), + ).withDependency(dependencyToUpgrade).withUseProperties(true).withSkipIfNewer(true) + .build() + + POMOperator.modify(context) + + assertTrue(context.pomFile.dirty, "Original POM File is Dirty") + + LOGGER.debug("original pom: {}", context.pomFile.pomDocument.asXML()) + LOGGER.debug("resulting pom: {}", context.pomFile.resultPom.asXML()) + + val diff = getXmlDifferences(context.pomFile.pomDocument, context.pomFile.resultPom) + + assertThat("Document has differences", diff.hasDifferences()) + + val differencesAsList = diff.differences.toList() + + assertThat("Document has several differences", differencesAsList.size > 1) + } + + @Test + fun testFileWithTabs() { + val dependencyToUpgrade = + Dependency("org.dom4j", "dom4j", version = "1.0.0") + + val originalPom = + "\n\n\t4.0.0\n\t\n\t\tbuild-utils\n\t\torg.modafocas.mojo\n\t\t0.0.1-SNAPSHOT\n\t\t../pom.xml\n\t\n\n\tderby-maven-plugin\n\tmaven-plugin\n\n\t\n\t\t\n\t\t\torg.apache.maven\n\t\t\tmaven-plugin-api\n\t\t\t2.0\n\t\t\n\t\t\n\t\t\tjunit\n\t\t\tjunit\n\t\t\t3.8.1\n\t\t\ttest\n\t\t\n\t\t\n\t\t\torg.apache.derby\n\t\t\tderby\n\t\t\t\${derbyVersion}\n\t\t\n\t\t\n\t\t\torg.apache.derby\n\t\t\tderbynet\n\t\t\t\${derbyVersion}\n\t\t\n\t\t\n\t\t\torg.apache.derby\n\t\t\tderbyclient\n\t\t\t\${derbyVersion}\n\t\t\n\t\t\n\t\t\tcommons-io\n\t\t\tcommons-io\n\t\t\t1.4\n\t\t\tjar\n\t\t\tcompile\n\t\t\n\t\n\n\t\n\t\t10.6.2.1\n\t\n\n" + + val context = + ProjectModelFactory.load( + originalPom.byteInputStream(), + ).withDependency(dependencyToUpgrade).withUseProperties(true).withSkipIfNewer(true) + .build() + + POMOperator.modify(context) + + val resultPom = context.pomFile.resultPomBytes.toString(Charset.defaultCharset()) + + assertTrue(context.pomFile.dirty, "Original POM File is Dirty") + + // aldrin: uncomment this to check out formatting - useful for the next section + // println(StringEscapeUtils.escapeJava(resultPom)) + + assertThat( + "Document should have a tab-based string", + resultPom.contains("\n\t\t\n\t\t\torg.dom4j\n\t\t\tdom4j\n\t\t\n") + ) + } + +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMScannerTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMScannerTest.kt new file mode 100644 index 000000000..c6eb2add1 --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/POMScannerTest.kt @@ -0,0 +1,106 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.InvalidPathException +import io.github.pixee.maven.operator.POMScanner +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.io.File +import kotlin.test.assertTrue +import kotlin.test.fail + +class POMScannerTest: AbstractTestBase() { + val currentDirectory = File(System.getProperty("user.dir")) + + @Test + fun testBasic() { + val pomFile = getResourceAsFile("sample-child-with-relativepath.xml") + + val pmf = POMScanner.scanFrom(pomFile, currentDirectory) + } + + @Test + fun testTwoLevelsWithLoop() { + val pomFile = getResourceAsFile("sample-child-with-relativepath-and-two-levels.xml") + + assertThrows { POMScanner.scanFrom(pomFile, currentDirectory) } + } + + @Test + fun testTwoLevelsWithoutLoop() { + val pomFile = getResourceAsFile("sample-child-with-relativepath-and-two-levels-nonloop.xml") + + val pmf = POMScanner.scanFrom(pomFile, currentDirectory).build() + + assertTrue(pmf.parentPomFiles.size == 2, "There must be two parent pom files") + + val uniquePaths = pmf.allPomFiles.map { it.pomPath!!.toURI().normalize().toString() }.toSet() + + val uniquePathsAsString = uniquePaths.joinToString(" ") + + LOGGER.info("uniquePathsAsString: $uniquePathsAsString") + + assertTrue(uniquePaths.size == 3, "There must be three unique pom files referenced") + } + + @Test + fun testMultipleChildren() { + for (index in 1..3) { + val pomFile = getResourceAsFile("nested/child/pom/pom-$index-child.xml") + + val pm = POMScanner.scanFrom(pomFile, currentDirectory).build() + + assertTrue(pm.parentPomFiles.size == 2, "There must be two parent pom files") + + val uniquePaths = pm.allPomFiles.map { it.pomPath!!.toURI().normalize().toString() } + + val uniquePathsAsString = uniquePaths.joinToString(" ") + + LOGGER.info("uniquePathsAsString: $uniquePathsAsString") + + assertTrue(uniquePaths.size == 3, "There must be three unique pom files referenced") + } + } + + @Test + fun testInvalidRelativePaths() { + for (index in 1..3) { + val name = "sample-child-with-broken-path-${index}.xml" + val pomFile = getResourceAsFile(name) + + try { + POMScanner.scanFrom(pomFile, currentDirectory) + + fail("Unreachable code for file: $name") + } catch (e: Exception) { + LOGGER.info("Exception thrown: ", e) + + if (e is InvalidPathException) { + continue + } + + throw e + } + } + } + + @Test + fun testWithRelativePathEmpty() { + for (index in 3..4) { + val pomFile = getResourceAsFile("pom-multiple-pom-parent-level-${index}.xml") + + try { + val pmf = POMScanner.scanFrom(pomFile, currentDirectory) + + assertTrue(pmf.build().parentPomFiles.isNotEmpty()) + } catch (e: InvalidPathException) { + LOGGER.info("Exception thrown: ", e) + + if (e is InvalidPathException) { + continue + } + + throw e + } + } + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/PropertyResolutionTest.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/PropertyResolutionTest.kt new file mode 100644 index 000000000..2f7cc96ec --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/PropertyResolutionTest.kt @@ -0,0 +1,52 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.Dependency +import io.github.pixee.maven.operator.ProjectModelFactory +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class PropertyResolutionTest { + @Test + fun testPropertyResolutionWhenProfileIsDeactivatedForcefully() { + val resolvedProperties = resolveWithProfiles("!test-profile") + + Assertions.assertFalse(resolvedProperties.contains("foo"), "foo property must not be there") + } + + @Test + fun testPropertyResolutionWhenProfileIsMissing() { + val resolvedProperties = resolveWithProfiles() + + Assertions.assertFalse(resolvedProperties.contains("foo"), "foo property must not be there") + } + + @Test + fun testPropertyResolutionWhenProfileIsActivated() { + val resolvedProperties = resolveWithProfiles("test-profile") + + assertTrue(resolvedProperties.contains("foo"), "foo property must be there") + assertEquals(resolvedProperties["foo"], "bar", "foo property must be equal to 'bar'") + } + + private fun resolveWithProfiles(vararg profilesToUse: String): Map { + LOGGER.debug("resolving with profiles: {}", profilesToUse) + + val dependencyToUpgrade = Dependency("org.dom4j", "dom4j", version = "2.0.2") + val context = + ProjectModelFactory.load( + POMOperatorTest::class.java.getResource("pom-1.xml")!!, + ).withDependency(dependencyToUpgrade).withActiveProfiles(*profilesToUse).build() + + LOGGER.debug("Resolved Properties: {}", context.resolvedProperties) + + return context.resolvedProperties + } + + companion object { + private val LOGGER: Logger = LoggerFactory.getLogger(POMOperatorTest::class.java) + } +} diff --git a/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/util.kt b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/util.kt new file mode 100644 index 000000000..43669f4f6 --- /dev/null +++ b/pom-operator/src/test/kotlin/io/github/pixee/maven/operator/test/util.kt @@ -0,0 +1,63 @@ +package io.github.pixee.maven.operator.test + +import io.github.pixee.maven.operator.ProjectModel +import io.github.pixee.maven.operator.Util.which +import org.apache.commons.lang3.SystemUtils +import org.dom4j.Document +import org.dom4j.io.SAXReader +import java.io.File +import java.io.FileInputStream + + +internal fun ProjectModel.getRuntimeResolvedProperties(): Map = + this.getEffectivePom().rootElement.elements("properties").flatMap { it.elements() } + .associate { + it.name to it.text + } + +fun ProjectModel.getEffectivePom(): Document { + val tmpInputFile = File.createTempFile("tmp-pom-orig", ".xml") + + tmpInputFile.writeBytes(this.pomFile.resultPomBytes) + + val tmpOutputFile = File.createTempFile("tmp-pom", ".xml") + + val processArgs: MutableList = + mutableListOf( + which("mvn")!!.absolutePath, + "-B", + "-N", + "-f", + tmpInputFile.absolutePath, + ) + + if (SystemUtils.IS_OS_WINDOWS) { + processArgs.addAll(0, listOf("cmd.exe", "/c")) + } + + if (this.activeProfiles.isNotEmpty()) { + // TODO Aldrin: How safe is not to escape those things? My concern is that deactivating a profile uses '!', + // and I'm not sure how shell escaping rules play a part on that + processArgs.addAll(listOf("-P", this.activeProfiles.joinToString(","))) + } + + processArgs.addAll( + listOf( + "help:effective-pom", + "-Doutput=${tmpOutputFile.absolutePath}" + ) + ) + + val psBuilder = ProcessBuilder(processArgs).inheritIO() + + psBuilder.environment().putAll(System.getenv()) + + val process = psBuilder.start() + + val retCode = process.waitFor() + + if (0 != retCode) + throw IllegalStateException("Unexpected return code from maven: $retCode") + + return SAXReader().read(FileInputStream(tmpOutputFile)) +} diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/broken-pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/broken-pom.xml new file mode 100644 index 000000000..b50e5c53b --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/broken-pom.xml @@ -0,0 +1,3 @@ + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1-child.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1-child.xml new file mode 100644 index 000000000..e4a3f45d9 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1-child.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./pom-1.xml + + + pom-1-child + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1.xml new file mode 100644 index 000000000..1381aa96c --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-1.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + ../../super/pom/pom.xml + + + org-pom + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2-child.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2-child.xml new file mode 100644 index 000000000..777615260 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2-child.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./pom-2.xml + + + pom-2-child + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2.xml new file mode 100644 index 000000000..48050c8c6 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-2.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + ../../super/pom + + + org-pom + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3-child.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3-child.xml new file mode 100644 index 000000000..4e887529c --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3-child.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./pom-3.xml + + + pom-3-child + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3.xml new file mode 100644 index 000000000..d64713cd7 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/child/pom/pom-3.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + ../../super/pom/other-pom.xml + + + org-pom + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/other-pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/other-pom.xml new file mode 100644 index 000000000..59d61dcba --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/other-pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + 1 + super-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/pom.xml new file mode 100644 index 000000000..5b9c4e081 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/nested/super/pom/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + 1 + org-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-child-broken.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-child-broken.xml new file mode 100644 index 000000000..cd59a5c84 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-child-broken.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent-broken.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent-broken.xml new file mode 100644 index 000000000..c12b8458f --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent-broken.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + pom-operator + 1 + ./parent-and-child-parent.xml + + + pom-operator + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent.xml new file mode 100644 index 000000000..e27c9a221 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/parent-and-child-parent.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 1 + pom + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-1.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-1.xml new file mode 100644 index 000000000..7b8471751 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-1.xml @@ -0,0 +1,159 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + test-profile + + bar + + + + + + + org.dom4j + dom4j + 2.1.3 + + + jaxen + jaxen + 1.2.0 + + + xerces + xercesImpl + 2.12.1 + + + io.github.java-diff-utils + java-diff-utils + 4.9 + + + junit + junit + 4.11 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-3.xml new file mode 100644 index 000000000..80ded0522 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-3.xml @@ -0,0 +1,509 @@ + + 4.0.0 + + + org.codehaus.mojo + mojo-parent + 63 + + + versions-maven-plugin + 2.9-SNAPSHOT + maven-plugin + + Versions Maven Plugin + http://www.mojohaus.org/versions-maven-plugin/ + + Versions Plugin for Maven. The Versions Plugin updates the versions of components in the + POM. + + 2008 + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Stephen Connolly + stephenconnolly at codehaus + + Lead Developer + + 0 + + + Paul Gier + pgier at redhat + + Developer + + + + Arnaud Heritier + aheritier at apache + + Developer + + +1 + + + + + Benoit Lafontaine + +1 + + + Martin Franklin + + + Tom Folga + + + Eric Pabst + + + Stefan Seelmann + + + Clement Denis + + + Erik Schepers + + + Anton Johansson + antoon.johansson@gmail.com + +1 + + + + + 3.0 + + + + scm:git:https://github.com/mojohaus/versions-maven-plugin.git + scm:git:ssh://git@github.com/mojohaus/versions-maven-plugin.git + + https://github.com/mojohaus/versions-maven-plugin/tree/${project.scm.tag} + versions-maven-plugin-2.8.1 + + + github + https://github.com/mojohaus/versions-maven-plugin/issues/ + + + + 1.8 + ${mojo.java.target} + 5.8.1 + 3.0.5 + 3.4.0 + 1.10 + 1.10 + ${project.version} + 3.7 + 2020-08-07T21:31:00Z + + + + + + org.junit + junit-bom + ${junitBomVersion} + pom + import + + + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.1 + provided + + + + org.apache.maven + maven-artifact + ${mavenVersion} + + + org.apache.maven + maven-core + ${mavenVersion} + + + org.apache.maven + maven-compat + ${mavenVersion} + + + org.apache.maven + maven-model + ${mavenVersion} + + + org.apache.maven + maven-plugin-api + ${mavenVersion} + + + org.apache.maven + maven-settings + ${mavenVersion} + + + org.apache.maven.reporting + maven-reporting-api + 3.0 + + + org.apache.maven.reporting + maven-reporting-impl + 3.0.0 + + + org.apache.maven.shared + maven-common-artifact-filters + 3.2.0 + + + org.apache.maven.wagon + wagon-provider-api + ${wagonVersion} + + + org.apache.maven.wagon + wagon-file + ${wagonVersion} + + + + + org.apache.maven.doxia + doxia-core + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-sink-api + ${doxiaVersion} + + + + + org.apache.maven.doxia + doxia-site-renderer + ${doxia-sitetoolsVersion} + + + org.codehaus.plexus + plexus-container-default + + + org.codehaus.plexus + plexus-component-api + + + + + + org.codehaus.plexus + plexus-utils + 3.4.1 + + + org.codehaus.plexus + plexus-container-default + 2.1.0 + + + org.codehaus.plexus + plexus-interactivity-api + 1.1 + + + plexus-container-default + org.codehaus.plexus + + + + + com.fasterxml.woodstox + woodstox-core + 6.2.6 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.hamcrest + hamcrest-core + + + + + org.mockito + mockito-core + 3.12.4 + test + + + org.hamcrest + hamcrest + 2.2 + test + + + + + + + + org.codehaus.modello + modello-maven-plugin + 1.11 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + true + + + + + helpmojo + descriptor + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + check-java18 + test + + check + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + + + org.codehaus.modello + modello-maven-plugin + + + generate-sources + generate-sources + + + xpp3-reader + + xpp3-writer + + java + + + + site-doc + pre-site + + xdoc + + + + site-xsd + pre-site + + xsd + + + + ${project.build.directory}/generated-site/resources/xsd + + + + + + + src/main/mdo/rule.mdo + + 2.0.0 + true + + + + org.codehaus.mojo + mrm-maven-plugin + 1.2.0 + + + + start + stop + + + + + repository.proxy.url + + + src/it-repo + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + + src/it + ${project.build.directory}/it + ${project.build.directory}/local-repo + src/it/settings.xml + true + true + + 1 + + */pom.xml + + verify + + ${repository.proxy.url} + + -Xmx256m + + + + maven-javadoc-plugin + + + + org.codehaus.mojo.versions.model, + org.codehaus.mojo.versions.model.io.xpp3 + + + + + + + + + + maven-javadoc-plugin + + ${mojo.java.target} + + + + maven-invoker-plugin + + + + report + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + + + dependency-updates-report + plugin-updates-report + property-updates-report + + + + + + + + + + + run-its + + verify + + + org.apache.maven.plugins + maven-invoker-plugin + + + integration-test + + install + run + + + false + true + + + + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + + + + report + + + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-broken.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-broken.xml new file mode 100644 index 000000000..58952de39 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-broken.xml @@ -0,0 +1,5 @@ + + + missing + 4.0.0 + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1-result.xml new file mode 100644 index 000000000..ff03447ec --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1-result.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + org.dom4j + dom4j + 2.0.3 + + + + + + org.dom4j + dom4j + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1.xml new file mode 100644 index 000000000..cb6a84331 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-1.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3-result.xml new file mode 100644 index 000000000..3e79270b2 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3-result.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + org.dom4j + dom4j + 2.0.2 + + + jaxen + jaxen + 1.2.0 + + + xerces + xercesImpl + 2.12.1 + + + io.github.java-diff-utils + java-diff-utils + 4.9 + + + junit + junit + 4.11 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3.xml new file mode 100644 index 000000000..6de228aa5 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-3.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + org.dom4j + dom4j + 2.1.3 + + + jaxen + jaxen + 1.2.0 + + + xerces + xercesImpl + 2.12.1 + + + io.github.java-diff-utils + java-diff-utils + 4.9 + + + junit + junit + 4.11 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4-result.xml new file mode 100644 index 000000000..324a9a7ec --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4-result.xml @@ -0,0 +1,242 @@ + + 4.0.0 + webgoat-server + jar + + org.owasp.webgoat + webgoat-parent + 8.2.3-SNAPSHOT + + + + org.owasp.webgoat.StartWebGoat + + + + + org.owasp.webgoat + webgoat-container + ${project.version} + + + org.owasp.webgoat.lesson + challenge + ${project.version} + + + org.owasp.webgoat.lesson + bypass-restrictions + ${project.version} + + + org.owasp.webgoat.lesson + client-side-filtering + ${project.version} + + + org.owasp.webgoat.lesson + crypto + ${project.version} + + + org.owasp.webgoat.lesson + cross-site-scripting + ${project.version} + + + org.owasp.webgoat.lesson + html-tampering + ${project.version} + + + org.owasp.webgoat.lesson + http-basics + ${project.version} + + + org.owasp.webgoat.lesson + http-proxies + ${project.version} + + + org.owasp.webgoat.lesson + cia + ${project.version} + + + org.owasp.webgoat.lesson + chrome-dev-tools + ${project.version} + + + org.owasp.webgoat.lesson + idor + ${project.version} + + + org.owasp.webgoat.lesson + csrf + ${project.version} + + + org.owasp.webgoat.lesson + insecure-login + ${project.version} + + + org.owasp.webgoat.lesson + insecure-deserialization + ${project.version} + + + org.owasp.webgoat.lesson + jwt + ${project.version} + + + org.owasp.webgoat.lesson + path-traversal + ${project.version} + + + org.owasp.webgoat.lesson + sql-injection + ${project.version} + + + org.owasp.webgoat.lesson + vulnerable-components + ${project.version} + + + org.owasp.webgoat.lesson + xxe + ${project.version} + + + org.owasp.webgoat.lesson + auth-bypass + ${project.version} + + + org.owasp.webgoat.lesson + webgoat-introduction + ${project.version} + + + org.owasp.webgoat.lesson + webwolf-introduction + ${project.version} + + + org.owasp.webgoat.lesson + missing-function-ac + ${project.version} + + + org.owasp.webgoat.lesson + password-reset + ${project.version} + + + org.owasp.webgoat.lesson + ssrf + ${project.version} + + + org.owasp.webgoat.lesson + secure-passwords + ${project.version} + + + org.owasp.webgoat.lesson + spoof-cookie + ${project.version} + + + org.owasp.webgoat.lesson + hijack-session + ${project.version} + + + org.owasp.webgoat.lesson + webgoat-lesson-template + ${project.version} + + + org.owasp.webgoat.lesson + logging + ${project.version} + + + org.springframework.boot + spring-boot-devtools + true + + + org.postgresql + postgresql + + + org.apache.activemq + activemq-amqp + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + org.thymeleaf.extra + thymeleaf-extras-springsecurity5++ + + + org.asciidoctor + asciidoctorj + + + org.jruby + jruby-complete + + + + + + true + + + + org.apache.maven.plugins + maven-jar-plugin + + + test-compile + + jar + + + internal + + + + + + + + + + org.apache.activemq + activemq-amqp + 5.16.2 + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4.xml new file mode 100644 index 000000000..2b6f8f8a4 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-4.xml @@ -0,0 +1,230 @@ + + 4.0.0 + webgoat-server + jar + + org.owasp.webgoat + webgoat-parent + 8.2.3-SNAPSHOT + + + + org.owasp.webgoat.StartWebGoat + + + + + org.owasp.webgoat + webgoat-container + ${project.version} + + + org.owasp.webgoat.lesson + challenge + ${project.version} + + + org.owasp.webgoat.lesson + bypass-restrictions + ${project.version} + + + org.owasp.webgoat.lesson + client-side-filtering + ${project.version} + + + org.owasp.webgoat.lesson + crypto + ${project.version} + + + org.owasp.webgoat.lesson + cross-site-scripting + ${project.version} + + + org.owasp.webgoat.lesson + html-tampering + ${project.version} + + + org.owasp.webgoat.lesson + http-basics + ${project.version} + + + org.owasp.webgoat.lesson + http-proxies + ${project.version} + + + org.owasp.webgoat.lesson + cia + ${project.version} + + + org.owasp.webgoat.lesson + chrome-dev-tools + ${project.version} + + + org.owasp.webgoat.lesson + idor + ${project.version} + + + org.owasp.webgoat.lesson + csrf + ${project.version} + + + org.owasp.webgoat.lesson + insecure-login + ${project.version} + + + org.owasp.webgoat.lesson + insecure-deserialization + ${project.version} + + + org.owasp.webgoat.lesson + jwt + ${project.version} + + + org.owasp.webgoat.lesson + path-traversal + ${project.version} + + + org.owasp.webgoat.lesson + sql-injection + ${project.version} + + + org.owasp.webgoat.lesson + vulnerable-components + ${project.version} + + + org.owasp.webgoat.lesson + xxe + ${project.version} + + + org.owasp.webgoat.lesson + auth-bypass + ${project.version} + + + org.owasp.webgoat.lesson + webgoat-introduction + ${project.version} + + + org.owasp.webgoat.lesson + webwolf-introduction + ${project.version} + + + org.owasp.webgoat.lesson + missing-function-ac + ${project.version} + + + org.owasp.webgoat.lesson + password-reset + ${project.version} + + + org.owasp.webgoat.lesson + ssrf + ${project.version} + + + org.owasp.webgoat.lesson + secure-passwords + ${project.version} + + + org.owasp.webgoat.lesson + spoof-cookie + ${project.version} + + + org.owasp.webgoat.lesson + hijack-session + ${project.version} + + + org.owasp.webgoat.lesson + webgoat-lesson-template + ${project.version} + + + org.owasp.webgoat.lesson + logging + ${project.version} + + + org.springframework.boot + spring-boot-devtools + true + + + org.postgresql + postgresql + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + org.thymeleaf.extra + thymeleaf-extras-springsecurity5++ + + + org.asciidoctor + asciidoctorj + + + org.jruby + jruby-complete + + + + + + true + + + + org.apache.maven.plugins + maven-jar-plugin + + + test-compile + + jar + + + internal + + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5-result.xml new file mode 100644 index 000000000..0f3bdca62 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5-result.xml @@ -0,0 +1,820 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + org.owasp.webgoat + webgoat + 2023.5-SNAPSHOT + jar + + WebGoat + WebGoat, a deliberately insecure Web Application + https://github.com/WebGoat/WebGoat + 2006 + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD + https://github.com/WebGoat/WebGoat + + + + Github Issues + https://github.com/WebGoat/WebGoat/issues + + + + + 2.5.3 + 3.3.7 + 2.2 + + 3.1.2 + 3.2.1 + 2.11.0 + 3.12.0 + 1.10.0 + 30.1-jre + 0.8.8 + 17 + 0.9.1 + 0.9.3 + 3.5.1 + 1.15.4 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M9 + 17 + 17 + 3.15.0 + + UTF-8 + UTF-8 + 3.0.15.RELEASE + 5.3.2 + 8080 + 9090 + 2.27.2 + 1.2 + 1.4.5 + + 1.5.2 + 1.0.2 + + + + + + + org.ow2.asm + asm + 9.1 + + + + org.apache.commons + commons-exec + 1.3 + + + org.asciidoctor + asciidoctorj + ${asciidoctorj.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + com.nulab-inc + zxcvbn + ${zxcvbn.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + + + cglib + cglib-nodep + ${cglib.version} + + + xml-resolver + xml-resolver + ${xml-resolver.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + com.google.guava + guava + ${guava.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + com.github.tomakehurst + wiremock + ${wiremock.version} + + + io.github.bonigarcia + webdrivermanager + ${webdriver.version} + + + org.apache.commons + commons-compress + 1.22 + + + org.jruby + jruby + 9.3.6.0 + + + io.github.pixee + java-security-toolkit + ${versions.java-security-toolkit} + + + + + + + org.apache.commons + commons-exec + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + javax.xml.bind + jaxb-api + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.flywaydb + flyway-core + + + org.asciidoctor + asciidoctorj + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + + + org.jsoup + jsoup + + + com.nulab-inc + zxcvbn + + + com.thoughtworks.xstream + xstream + + + cglib + cglib-nodep + + + xml-resolver + xml-resolver + + + io.jsonwebtoken + jjwt + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-text + + + org.bitbucket.b_c + jose4j + + + org.webjars + bootstrap + + + org.webjars + jquery + + + org.glassfish.jaxb + jaxb-runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.github.tomakehurst + wiremock + test + + + io.rest-assured + rest-assured + test + + + io.github.pixee + java-security-toolkit + + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + org.owasp.webgoat.server.StartWebGoat + + + + org.asciidoctor + asciidoctorj + + + + + + + repackage + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source-as-test-sources + + add-test-source + + generate-test-sources + + + src/it/java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${basedir}/src/test/resources/logback-test.xml + + -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} + org/owasp/webgoat/*Test + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + com.diffplug.spotless + spotless-maven-plugin + 2.33.0 + + + + + .gitignore + + + + + true + 4 + + + + + + **/*.md + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + src/it/java/**/*.java + + + + + true + + + + + UTF-8 + ${line.separator} + true + false + true + 2 + false + false + recommended_2008_06 + true + true + true + + + + + + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.2.1 + + + restrict-log4j-versions + + enforce + + validate + + + + + org.apache.logging.log4j:log4j-core + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + + + local-server + + + start-server + + true + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + reserve-container-port + + reserve-network-port + + process-resources + + + webgoat.port + webwolf.port + jmxPort + + + + + + + com.bazaarvoice.maven.plugins + process-exec-maven-plugin + 0.9 + + + start-jar + + start + + pre-integration-test + + ${project.build.directory} + + java + -jar + -Dlogging.pattern.console= + -Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dspring.main.banner-mode=off + -Dwebgoat.port=${webgoat.port} + -Dwebwolf.port=${webwolf.port} + --add-opens + java.base/java.lang=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.base/java.text=ALL-UNNAMED + --add-opens + java.desktop/java.beans=ALL-UNNAMED + --add-opens + java.desktop/java.awt.font=ALL-UNNAMED + --add-opens + java.base/sun.nio.ch=ALL-UNNAMED + --add-opens + java.base/java.io=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + ${project.build.directory}/webgoat-${project.version}.jar + + false + http://localhost:${webgoat.port}/WebGoat/actuator/health + + + + stop-jar-process + + stop-all + + post-integration-test + + + + + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.5.1 + + 7 + false + false + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + check + + + + + + + + + + coverage + + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + ${surefire.jacoco.args} + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + before-unit-test + + prepare-agent + + + ${project.build.directory}/jacoco/jacoco-ut.exec + surefire.jacoco.args + + + + check + + check + + + + + BUNDLE + + + CLASS + COVEREDCOUNT + 0.6 + + + + + ${project.build.directory}/jacoco/jacoco-ut.exec + + + + after-unit-test + + report + + test + + ${project.build.directory}/jacoco/jacoco-ut.exec + ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report + + + + + + + + + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5.xml new file mode 100644 index 000000000..24b8db115 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-5.xml @@ -0,0 +1,810 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + org.owasp.webgoat + webgoat + 2023.5-SNAPSHOT + jar + + WebGoat + WebGoat, a deliberately insecure Web Application + https://github.com/WebGoat/WebGoat + 2006 + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD + https://github.com/WebGoat/WebGoat + + + + Github Issues + https://github.com/WebGoat/WebGoat/issues + + + + + 2.5.3 + 3.3.7 + 2.2 + + 3.1.2 + 3.2.1 + 2.11.0 + 3.12.0 + 1.10.0 + 30.1-jre + 0.8.8 + 17 + 0.9.1 + 0.9.3 + 3.5.1 + 1.15.4 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M9 + 17 + 17 + 3.15.0 + + UTF-8 + UTF-8 + 3.0.15.RELEASE + 5.3.2 + 8080 + 9090 + 2.27.2 + 1.2 + 1.4.5 + + 1.5.2 + + + + + + + org.ow2.asm + asm + 9.1 + + + + org.apache.commons + commons-exec + 1.3 + + + org.asciidoctor + asciidoctorj + ${asciidoctorj.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + com.nulab-inc + zxcvbn + ${zxcvbn.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + + + cglib + cglib-nodep + ${cglib.version} + + + xml-resolver + xml-resolver + ${xml-resolver.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + com.google.guava + guava + ${guava.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + com.github.tomakehurst + wiremock + ${wiremock.version} + + + io.github.bonigarcia + webdrivermanager + ${webdriver.version} + + + org.apache.commons + commons-compress + 1.22 + + + org.jruby + jruby + 9.3.6.0 + + + + + + + org.apache.commons + commons-exec + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + javax.xml.bind + jaxb-api + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.flywaydb + flyway-core + + + org.asciidoctor + asciidoctorj + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + + + org.jsoup + jsoup + + + com.nulab-inc + zxcvbn + + + com.thoughtworks.xstream + xstream + + + cglib + cglib-nodep + + + xml-resolver + xml-resolver + + + io.jsonwebtoken + jjwt + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-text + + + org.bitbucket.b_c + jose4j + + + org.webjars + bootstrap + + + org.webjars + jquery + + + org.glassfish.jaxb + jaxb-runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.github.tomakehurst + wiremock + test + + + io.rest-assured + rest-assured + test + + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + org.owasp.webgoat.server.StartWebGoat + + + + org.asciidoctor + asciidoctorj + + + + + + + repackage + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source-as-test-sources + + add-test-source + + generate-test-sources + + + src/it/java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${basedir}/src/test/resources/logback-test.xml + + -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} + org/owasp/webgoat/*Test + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + com.diffplug.spotless + spotless-maven-plugin + 2.33.0 + + + + + .gitignore + + + + + true + 4 + + + + + + **/*.md + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + src/it/java/**/*.java + + + + + true + + + + + UTF-8 + ${line.separator} + true + false + true + 2 + false + false + recommended_2008_06 + true + true + true + + + + + + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.2.1 + + + restrict-log4j-versions + + enforce + + validate + + + + + org.apache.logging.log4j:log4j-core + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + + + local-server + + + start-server + + true + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + reserve-container-port + + reserve-network-port + + process-resources + + + webgoat.port + webwolf.port + jmxPort + + + + + + + com.bazaarvoice.maven.plugins + process-exec-maven-plugin + 0.9 + + + start-jar + + start + + pre-integration-test + + ${project.build.directory} + + java + -jar + -Dlogging.pattern.console= + -Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dspring.main.banner-mode=off + -Dwebgoat.port=${webgoat.port} + -Dwebwolf.port=${webwolf.port} + --add-opens + java.base/java.lang=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.base/java.text=ALL-UNNAMED + --add-opens + java.desktop/java.beans=ALL-UNNAMED + --add-opens + java.desktop/java.awt.font=ALL-UNNAMED + --add-opens + java.base/sun.nio.ch=ALL-UNNAMED + --add-opens + java.base/java.io=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + ${project.build.directory}/webgoat-${project.version}.jar + + false + http://localhost:${webgoat.port}/WebGoat/actuator/health + + + + stop-jar-process + + stop-all + + post-integration-test + + + + + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.5.1 + + 7 + false + false + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + check + + + + + + + + + + coverage + + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + ${surefire.jacoco.args} + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + before-unit-test + + prepare-agent + + + ${project.build.directory}/jacoco/jacoco-ut.exec + surefire.jacoco.args + + + + check + + check + + + + + BUNDLE + + + CLASS + COVEREDCOUNT + 0.6 + + + + + ${project.build.directory}/jacoco/jacoco-ut.exec + + + + after-unit-test + + report + + test + + ${project.build.directory}/jacoco/jacoco-ut.exec + ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report + + + + + + + + + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6-result.xml new file mode 100644 index 000000000..8be353890 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6-result.xml @@ -0,0 +1,824 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + org.owasp.webgoat + webgoat + 2023.5-SNAPSHOT + jar + + WebGoat + WebGoat, a deliberately insecure Web Application + https://github.com/WebGoat/WebGoat + 2006 + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD + https://github.com/WebGoat/WebGoat + + + + Github Issues + https://github.com/WebGoat/WebGoat/issues + + + + + 2.5.3 + 3.3.7 + 2.2 + + 3.1.2 + 3.2.1 + 2.11.0 + 3.12.0 + 1.10.0 + 30.1-jre + 0.8.8 + 17 + 0.9.1 + 0.9.3 + 3.5.1 + 1.15.4 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M9 + 17 + 17 + 3.15.0 + + UTF-8 + UTF-8 + 3.0.15.RELEASE + 5.3.2 + 8080 + 9090 + 2.27.2 + 1.2 + 1.4.5 + + 1.5.2 + 1.0.2 + + + + + + + org.ow2.asm + asm + 9.1 + + + + org.apache.commons + commons-exec + 1.3 + + + org.asciidoctor + asciidoctorj + ${asciidoctorj.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + com.nulab-inc + zxcvbn + ${zxcvbn.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + + + cglib + cglib-nodep + ${cglib.version} + + + xml-resolver + xml-resolver + ${xml-resolver.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + com.google.guava + guava + ${guava.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + com.github.tomakehurst + wiremock + ${wiremock.version} + + + io.github.bonigarcia + webdrivermanager + ${webdriver.version} + + + org.apache.commons + commons-compress + 1.22 + + + org.jruby + jruby + 9.3.6.0 + + + io.github.pixee + java-security-toolkit + ${versions.java-security-toolkit} + + + + + + + org.apache.commons + commons-exec + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + javax.xml.bind + jaxb-api + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.flywaydb + flyway-core + + + org.asciidoctor + asciidoctorj + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + + + org.jsoup + jsoup + + + com.nulab-inc + zxcvbn + + + com.thoughtworks.xstream + xstream + + + cglib + cglib-nodep + + + xml-resolver + xml-resolver + + + io.jsonwebtoken + jjwt + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-text + + + org.bitbucket.b_c + jose4j + + + org.webjars + bootstrap + + + org.webjars + jquery + + + org.glassfish.jaxb + jaxb-runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.github.tomakehurst + wiremock + test + + + io.rest-assured + rest-assured + test + + + io.github.pixee + java-security-toolkit + + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + org.owasp.webgoat.server.StartWebGoat + + + + org.asciidoctor + asciidoctorj + + + + + + + repackage + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source-as-test-sources + + add-test-source + + generate-test-sources + + + src/it/java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${basedir}/src/test/resources/logback-test.xml + + -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} + org/owasp/webgoat/*Test + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + com.diffplug.spotless + spotless-maven-plugin + 2.33.0 + + + + + .gitignore + + + + + true + 4 + + + + + + **/*.md + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + src/it/java/**/*.java + + + + + true + + + + + UTF-8 + ${line.separator} + true + false + true + 2 + false + false + recommended_2008_06 + true + true + true + + + + + + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.2.1 + + + restrict-log4j-versions + + enforce + + validate + + + + + org.apache.logging.log4j:log4j-core + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + + + local-server + + + start-server + + true + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + reserve-container-port + + reserve-network-port + + process-resources + + + webgoat.port + webwolf.port + jmxPort + + + + + + + com.bazaarvoice.maven.plugins + process-exec-maven-plugin + 0.9 + + + start-jar + + start + + pre-integration-test + + ${project.build.directory} + + java + -jar + -Dlogging.pattern.console= + -Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dspring.main.banner-mode=off + -Dwebgoat.port=${webgoat.port} + -Dwebwolf.port=${webwolf.port} + --add-opens + java.base/java.lang=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.base/java.text=ALL-UNNAMED + --add-opens + java.desktop/java.beans=ALL-UNNAMED + --add-opens + java.desktop/java.awt.font=ALL-UNNAMED + --add-opens + java.base/sun.nio.ch=ALL-UNNAMED + --add-opens + java.base/java.io=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + ${project.build.directory}/webgoat-${project.version}.jar + + false + http://localhost:${webgoat.port}/WebGoat/actuator/health + + + + stop-jar-process + + stop-all + + post-integration-test + + + + + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.5.1 + + 7 + false + false + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + check + + + + + + + + + + coverage + + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + ${surefire.jacoco.args} + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + before-unit-test + + prepare-agent + + + ${project.build.directory}/jacoco/jacoco-ut.exec + surefire.jacoco.args + + + + check + + check + + + + + BUNDLE + + + CLASS + COVEREDCOUNT + 0.6 + + + + + ${project.build.directory}/jacoco/jacoco-ut.exec + + + + after-unit-test + + report + + test + + ${project.build.directory}/jacoco/jacoco-ut.exec + ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report + + + + + + + + + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6.xml new file mode 100644 index 000000000..de2f19e0c --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-6.xml @@ -0,0 +1,814 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + org.owasp.webgoat + webgoat + 2023.5-SNAPSHOT + jar + + WebGoat + WebGoat, a deliberately insecure Web Application + https://github.com/WebGoat/WebGoat + 2006 + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD + https://github.com/WebGoat/WebGoat + + + + Github Issues + https://github.com/WebGoat/WebGoat/issues + + + + + 2.5.3 + 3.3.7 + 2.2 + + 3.1.2 + 3.2.1 + 2.11.0 + 3.12.0 + 1.10.0 + 30.1-jre + 0.8.8 + 17 + 0.9.1 + 0.9.3 + 3.5.1 + 1.15.4 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M9 + 17 + 17 + 3.15.0 + + UTF-8 + UTF-8 + 3.0.15.RELEASE + 5.3.2 + 8080 + 9090 + 2.27.2 + 1.2 + 1.4.5 + + 1.5.2 + + + + + + + org.ow2.asm + asm + 9.1 + + + + org.apache.commons + commons-exec + 1.3 + + + org.asciidoctor + asciidoctorj + ${asciidoctorj.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + com.nulab-inc + zxcvbn + ${zxcvbn.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + + + cglib + cglib-nodep + ${cglib.version} + + + xml-resolver + xml-resolver + ${xml-resolver.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + com.google.guava + guava + ${guava.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + com.github.tomakehurst + wiremock + ${wiremock.version} + + + io.github.bonigarcia + webdrivermanager + ${webdriver.version} + + + org.apache.commons + commons-compress + 1.22 + + + org.jruby + jruby + 9.3.6.0 + + + + + + + org.apache.commons + commons-exec + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + javax.xml.bind + jaxb-api + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.flywaydb + flyway-core + + + org.asciidoctor + asciidoctorj + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + + + org.jsoup + jsoup + + + com.nulab-inc + zxcvbn + + + com.thoughtworks.xstream + xstream + + + cglib + cglib-nodep + + + xml-resolver + xml-resolver + + + io.jsonwebtoken + jjwt + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-text + + + org.bitbucket.b_c + jose4j + + + org.webjars + bootstrap + + + org.webjars + jquery + + + org.glassfish.jaxb + jaxb-runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.github.tomakehurst + wiremock + test + + + io.rest-assured + rest-assured + test + + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + org.owasp.webgoat.server.StartWebGoat + + + + org.asciidoctor + asciidoctorj + + + + + + + repackage + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source-as-test-sources + + add-test-source + + generate-test-sources + + + src/it/java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${basedir}/src/test/resources/logback-test.xml + + -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} + org/owasp/webgoat/*Test + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + com.diffplug.spotless + spotless-maven-plugin + 2.33.0 + + + + + .gitignore + + + + + true + 4 + + + + + + **/*.md + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + src/it/java/**/*.java + + + + + true + + + + + UTF-8 + ${line.separator} + true + false + true + 2 + false + false + recommended_2008_06 + true + true + true + + + + + + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.2.1 + + + restrict-log4j-versions + + enforce + + validate + + + + + org.apache.logging.log4j:log4j-core + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + + + local-server + + + start-server + + true + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + reserve-container-port + + reserve-network-port + + process-resources + + + webgoat.port + webwolf.port + jmxPort + + + + + + + com.bazaarvoice.maven.plugins + process-exec-maven-plugin + 0.9 + + + start-jar + + start + + pre-integration-test + + ${project.build.directory} + + java + -jar + -Dlogging.pattern.console= + -Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dspring.main.banner-mode=off + -Dwebgoat.port=${webgoat.port} + -Dwebwolf.port=${webwolf.port} + --add-opens + java.base/java.lang=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.base/java.text=ALL-UNNAMED + --add-opens + java.desktop/java.beans=ALL-UNNAMED + --add-opens + java.desktop/java.awt.font=ALL-UNNAMED + --add-opens + java.base/sun.nio.ch=ALL-UNNAMED + --add-opens + java.base/java.io=ALL-UNNAMED + --add-opens + java.base/java.util=ALL-UNNAMED + ${project.build.directory}/webgoat-${project.version}.jar + + false + http://localhost:${webgoat.port}/WebGoat/actuator/health + + + + stop-jar-process + + stop-all + + post-integration-test + + + + + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.5.1 + + 7 + false + false + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + check + + + + + + + + + + coverage + + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + ${surefire.jacoco.args} + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + before-unit-test + + prepare-agent + + + ${project.build.directory}/jacoco/jacoco-ut.exec + surefire.jacoco.args + + + + check + + check + + + + + BUNDLE + + + CLASS + COVEREDCOUNT + 0.6 + + + + + ${project.build.directory}/jacoco/jacoco-ut.exec + + + + after-unit-test + + report + + test + + ${project.build.directory}/jacoco/jacoco-ut.exec + ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report + + + + + + + + + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-property-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-property-result.xml new file mode 100644 index 000000000..55e673dca --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-property-result.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + 1.0.0 + + + + + test-profile + + 1.0.1 + + + + + + + org.dom4j + dom4j + ${sample.version} + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-tabs-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-tabs-result.xml new file mode 100644 index 000000000..d9ce1162a --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-case-with-tabs-result.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + build-utils + org.modafocas.mojo + 0.0.1-SNAPSHOT + ../pom.xml + + + derby-maven-plugin + maven-plugin + + + + org.apache.maven + maven-plugin-api + 2.0 + + + junit + junit + 3.8.1 + test + + + org.apache.derby + derby + ${derbyVersion} + + + org.apache.derby + derbynet + ${derbyVersion} + + + org.apache.derby + derbyclient + ${derbyVersion} + + + commons-io + commons-io + 1.4 + jar + compile + + + org.dom4j + dom4j + + + + + 10.6.2.1 + 1.0.0 + + + + + org.dom4j + dom4j + ${versions.dom4j} + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-no-version-property-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-no-version-property-result.xml new file mode 100644 index 000000000..3a98f80f1 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-no-version-property-result.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ./sample-parent + + + sample-child-with-relativepath + + + org.dom4j + dom4j + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-result.xml new file mode 100644 index 000000000..96bf62b22 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-result.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ./sample-parent + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-with-version-property-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-with-version-property-result.xml new file mode 100644 index 000000000..3a98f80f1 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-basic-with-version-property-result.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ./sample-parent + + + sample-child-with-relativepath + + + org.dom4j + dom4j + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-3.xml new file mode 100644 index 000000000..9548f8f86 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-3.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./sample-parent/pom-with-parent-level-3.xml + + + sample-child-with-relativepath + + + org.dom4j + dom4j + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-4.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-4.xml new file mode 100644 index 000000000..adaa98907 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-multiple-pom-parent-level-4.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./sample-parent/pom-with-parent-level-4.xml + + + sample-child-with-relativepath + + + org.dom4j + dom4j + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-pom-case-three-with-lower-version-result.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-pom-case-three-with-lower-version-result.xml new file mode 100644 index 000000000..6de228aa5 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-pom-case-three-with-lower-version-result.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + 1.5.31 + + + + + org.dom4j + dom4j + 2.1.3 + + + jaxen + jaxen + 1.2.0 + + + xerces + xercesImpl + 2.12.1 + + + io.github.java-diff-utils + java-diff-utils + 4.9 + + + junit + junit + 4.11 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-parent-level-1.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-parent-level-1.xml new file mode 100644 index 000000000..d00aa1387 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-parent-level-1.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + super-pom + 1 + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-property-simple.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-property-simple.xml new file mode 100644 index 000000000..d2f9f3b26 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/pom-with-property-simple.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + br.com.ingenieux + pom-operator + 0.0.1-SNAPSHOT + + + 0.0.1-SNAPSHOT + + + + + test-profile + + 1.0.1 + + + + + + + org.dom4j + dom4j + ${sample.version} + + + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-bad-pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-bad-pom.xml new file mode 100644 index 000000000..bd9dfcb76 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-bad-pom.xml @@ -0,0 +1,23 @@ + + + + 4.0.0 + + com.acme + testcode + 5.1 + + + + + + + org.slf4j + slf4j-api + 1.7.25 + + + + \ No newline at end of file diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-1.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-1.xml new file mode 100644 index 000000000..5c8086521 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-1.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ../../../../../../../../../../../../../../../../../../sample-parent + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-2.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-2.xml new file mode 100644 index 000000000..b0328f2ba --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-2.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + / + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-3.xml new file mode 100644 index 000000000..a8e03c78d --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-broken-path-3.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + C:/pom.xml + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-2.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-2.xml new file mode 100644 index 000000000..0a50cdd82 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-2.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ./sample-parent/other-pom.xml + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels-nonloop.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels-nonloop.xml new file mode 100644 index 000000000..be5836456 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels-nonloop.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./sample-parent/pom-with-parent-level-2-nonloop.xml + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels.xml new file mode 100644 index 000000000..6ee452f54 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath-and-two-levels.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + org-pom + 1 + ./sample-parent/pom-with-parent-level-2.xml + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath.xml new file mode 100644 index 000000000..96bf62b22 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-child-with-relativepath.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + br.com.ingenieux + sample-parent + 1 + ./sample-parent + + + sample-child-with-relativepath + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/other-pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/other-pom.xml new file mode 100644 index 000000000..6127e53d8 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/other-pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + sample-parent + 1 + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2-nonloop.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2-nonloop.xml new file mode 100644 index 000000000..a31f88775 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2-nonloop.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + ../superpom-nonloop.xml + + + org-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2.xml new file mode 100644 index 000000000..29b00a599 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-2.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + ../sample-child-with-relativepath-and-two-levels.xml + + + org-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-3.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-3.xml new file mode 100644 index 000000000..806cf4242 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-3.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + + + + org-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-4.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-4.xml new file mode 100644 index 000000000..7eda1ec72 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom-with-parent-level-4.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + + br.com.ingenieux + super-pom + 1 + + + + org-pom + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom.xml new file mode 100644 index 000000000..6127e53d8 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/sample-parent/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + sample-parent + 1 + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/superpom-nonloop.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/superpom-nonloop.xml new file mode 100644 index 000000000..d00aa1387 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/superpom-nonloop.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + br.com.ingenieux + super-pom + 1 + + pom + diff --git a/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/webgoat-parent.xml b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/webgoat-parent.xml new file mode 100644 index 000000000..4e8ab5de9 --- /dev/null +++ b/pom-operator/src/test/resources/io/github/pixee/maven/operator/test/webgoat-parent.xml @@ -0,0 +1,296 @@ + + + + 4.0.0 + org.owasp.webgoat + webgoat-parent + pom + 8.2.3-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + + WebGoat Parent Pom + Parent Pom for the WebGoat Project. A deliberately insecure Web Application + + 2006 + https://github.com/WebGoat/WebGoat + + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + https://github.com/WebGoat/WebGoat + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD + + + + Github Issues + https://github.com/WebGoat/WebGoat/issues + + + + + UTF-8 + UTF-8 + 17 + 17 + + + 2.5.2 + 3.2.1 + 3.12.0 + 2.6 + 30.1-jre + 1.18.20 + 2.27.2 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M5 + 17 + + + + webgoat-container + webgoat-lessons + webgoat-server + webwolf + webgoat-integration-tests + docker + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + org.apache.commons + commons-exec + 1.3 + + + javax.xml.bind + jaxb-api + + + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.2.5 + + + + + flatten + process-resources + + flatten + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 15 + 15 + UTF-8 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.2 + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.14.0 + + 15 + 1 + + + + ${maven.multiModuleProjectDirectory}/config/pmd/pmd-ruleset.xml + + + true + true + + + + + check + + + + + + + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.1.3 + + 7 + true + true + + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + + check + + + + + + + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + + + diff --git a/pom-operator/src/test/resources/simplelogger.properties b/pom-operator/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..a014e30b5 --- /dev/null +++ b/pom-operator/src/test/resources/simplelogger.properties @@ -0,0 +1,2 @@ +# suppress inspection "UnusedProperty" +org.slf4j.simpleLogger.defaultLogLevel=debug \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 29dee6ed2..75ff1a075 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -47,3 +47,4 @@ include("plugins:codemodder-plugin-llm") include("plugins:codemodder-plugin-maven") include("plugins:codemodder-plugin-pmd") include("plugins:codemodder-plugin-semgrep") +include("pom-operator")