diff --git a/README.md b/README.md index dfc01ac..38f9d9a 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,48 @@ # Nebula ArchRules -This repository contains several libraries of ArchRules which can be used in projects by using the [ArchRules Runner](https://github.com/nebula-plugins/nebula-archrules-plugin?tab=readme-ov-file#running-rules) plugin. +## ArchRule Building Blocks +This repository contains the `archrules-common` library which contains ArchUnit primitives (DescribedPredicates, ChainableFunctions, and ArchConditions) which assist in writing rules. These extend what comes out-of-the box in ArchUnit to cover more cases. +[![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-common?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-common/) -## Deprecation Rules +## ArchRule Libraries +This repository also contains several libraries of ArchRules which can be used in projects by using the [ArchRules Runner](https://github.com/nebula-plugins/nebula-archrules-plugin?tab=readme-ov-file#running-rules) plugin. + + +### Deprecation Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-deprecation?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-deprecation/) -## Gradle Plugin Development +### Gradle Plugin Development [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-gradle-plugin-development?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-gradle-plugin-development/) These rules enforce best practices when developing Gradle plugins. -## Guava Rules +### Guava Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-guava?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-guava/) These rules detect the usage of certain APIs from Guava which have standard library replacements. -## Javax Rules +### Javax Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-joda?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-javax/) These rules enforce the usage of `jakarta` over `javax`. -## Joda Rules +### Joda Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-joda?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-joda/) These rules enforce the usage of `java.time` over Joda Time. -## Nullability Rules +### Nullability Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-nullability?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-nullability/) These rules enforce JSpecify nullability annotations on public code. Kotlin classes are exempt from the rule, as Kotlin has nullability built into its type system, which is compatible with JSpecify. -## Security Rules +### Security Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-security?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-archrules-security/) These rules ensure calls are not made to known insecure OSS Java APIs. -## Testing Frameworks Rules +### Testing Frameworks Rules [![Maven Central](https://img.shields.io/maven-central/v/com.netflix.nebula/archrules-testing-frameworks?style=for-the-badge&color=01AF01)](https://repo1.maven.org/maven2/com/netflix/nebula/archrules-testing-frameworks/) These rules enforce upgrading to JUnit Jupiter. diff --git a/archrules-common/build.gradle.kts b/archrules-common/build.gradle.kts new file mode 100644 index 0000000..bc7695e --- /dev/null +++ b/archrules-common/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("com.netflix.nebula.library") +} +description = "Common Predicates and Chanable Functions for building rules" + +dependencies { + implementation(libs.jspecify) + api("com.tngtech.archunit:archunit:1.+") + + testImplementation(libs.assertj) + testImplementation(libs.logback) + testImplementation("org.jetbrains.kotlin:kotlin-stdlib:2.2.0") + testImplementation("com.netflix.nebula:nebula-archrules-core:0.+") +} +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} +tasks.named("compileTestJava") { + javaCompiler.set(javaToolchains.compilerFor { + languageVersion.set(JavaLanguageVersion.of(11)) + }) +} +dependencyLocking { + lockAllConfigurations() +} +testing { + suites { + named("test") { + useJUnitJupiter() + } + } +} diff --git a/archrules-common/gradle.lockfile b/archrules-common/gradle.lockfile new file mode 100644 index 0000000..c55bdf5 --- /dev/null +++ b/archrules-common/gradle.lockfile @@ -0,0 +1,30 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +ch.qos.logback:logback-classic:1.5.20=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.5.20=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +com.netflix.nebula:archrules-deprecation:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-joda:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-nullability:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-security:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-testing-frameworks:0.7.1=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:nebula-archrules-core:0.5.5=archRules,mainArchRulesRuntime +com.netflix.nebula:nebula-archrules-core:0.7.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit:1.4.1=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.17.7=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath +org.assertj:assertj-core:3.27.6=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:2.2.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.jetbrains:annotations:13.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.jspecify:jspecify:1.0.0=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.12.2=testArchRulesRuntime,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.12.2=testArchRulesRuntime,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.12.2=testArchRulesRuntime,testRuntimeClasspath +org.junit:junit-bom:5.12.2=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.17=archRules,compileClasspath,mainArchRulesRuntime,runtimeClasspath,testArchRulesRuntime,testCompileClasspath,testRuntimeClasspath +empty=annotationProcessor,testAnnotationProcessor diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/CanBeAnnotated.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/CanBeAnnotated.java new file mode 100644 index 0000000..18cb2f4 --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/CanBeAnnotated.java @@ -0,0 +1,30 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import org.jspecify.annotations.NullMarked; + +import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; + +@NullMarked +public class CanBeAnnotated { + public static class Predicates { + + /** + * Annotated with Java or Kotlin deprecation annotations + */ + public static DescribedPredicate deprecated() { + return annotatedWith(Deprecated.class) + .or(annotatedWith("kotlin.Deprecated")) + .or(annotatedWith("kotlin.DeprecatedSinceKotlin")) + .as("deprecated"); + } + + /** + * Annotated with Java deprecation annotation with the forRemoval property set to true + */ + public static DescribedPredicate deprecatedForRemoval() { + return annotatedWith(JavaAnnotation.Predicates.deprecatedForRemoval()) + .as("deprecated for removal"); + } + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/Dependency.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/Dependency.java new file mode 100644 index 0000000..9064b89 --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/Dependency.java @@ -0,0 +1,23 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class Dependency { + public static class Predicates { + + /** + * tests that a class and a dependency are in the same package + */ + public static DescribedPredicate resideInSamePackage() { + return new DescribedPredicate("reside in same package") { + @Override + public boolean test(com.tngtech.archunit.core.domain.Dependency dependency) { + return dependency.getOriginClass().getPackageName() + .equals(dependency.getTargetClass().getPackageName()); + } + }; + } + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAccess.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAccess.java new file mode 100644 index 0000000..765b386 --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAccess.java @@ -0,0 +1,24 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class JavaAccess { + public static class Predicates { + + /** + * tests that an access's origin and target are in the same package + */ + public static DescribedPredicate> targetHasOwnerInSamePackage() { + return new DescribedPredicate>( + "in the same package") { + @Override + public boolean test(com.tngtech.archunit.core.domain.JavaAccess javaAccess) { + return javaAccess.getOriginOwner().getPackage() + .equals(javaAccess.getTargetOwner().getPackage()); + } + }; + } + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAnnotation.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAnnotation.java new file mode 100644 index 0000000..0284e7b --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaAnnotation.java @@ -0,0 +1,28 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class JavaAnnotation { + public static class Predicates { + + /** + * Java deprecation annotation with the forRemoval property set to true + */ + public static DescribedPredicate> deprecatedForRemoval() { + return new DescribedPredicate>("@Deprecated(forRemoval=true)") { + @Override + public boolean test(com.tngtech.archunit.core.domain.JavaAnnotation annotation) { + if (!annotation.getRawType().isAssignableTo(Deprecated.class)) { + return false; + } + return annotation.get("forRemoval") + .filter(it -> it instanceof Boolean) + .map(value -> (Boolean) value) + .orElse(false); + } + }; + } + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaBeanGetterPredicate.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaBeanGetterPredicate.java new file mode 100644 index 0000000..88860df --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaBeanGetterPredicate.java @@ -0,0 +1,37 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.JavaModifier; +import org.jspecify.annotations.NullMarked; + +/** + * Matches getter methods (getX(), isX()) that follow JavaBeans naming conventions. + */ +@NullMarked +class JavaBeanGetterPredicate extends DescribedPredicate { + + JavaBeanGetterPredicate() { + super("getter"); + } + + @Override + public boolean test(JavaMethod input) { + if (input.getModifiers().contains(JavaModifier.STATIC)) { + return false; + } + if (!input.getParameters().isEmpty()) { + return false; + } + if (input.getName().startsWith("get")) { + return input.getName().length() >= 4 && !Character.isLowerCase(input.getName().charAt(3)); + } + if (input.getName().startsWith("is")) { + if (input.getRawReturnType().isAssignableTo(Boolean.class)) { + return false; + } + return !Character.isLowerCase(input.getName().charAt(2)); + } + return false; + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaClass.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaClass.java new file mode 100644 index 0000000..eab982b --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaClass.java @@ -0,0 +1,52 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaPackage; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.conditions.NebulaAnyDependencyCondition; +import org.jspecify.annotations.NullMarked; + +import java.lang.annotation.Annotation; + +import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_DIRECT_DEPENDENCIES_FROM_SELF; +import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_PACKAGE; +import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; +import static com.tngtech.archunit.lang.conditions.ArchPredicates.is; + +@NullMarked +public class JavaClass { + public static class Predicates { + /** + * evaluates the predicate on the class's package + */ + public static DescribedPredicate resideInAPackageThat( + DescribedPredicate condition) { + return condition.onResultOf(GET_PACKAGE) + .as("residing in package that %s", condition.getDescription()); + } + + /** + * checks if the class's package is annotated with a specific annotation + */ + public static DescribedPredicate resideInPackageAnnotatedWith( + Class annotationClass + ) { + return resideInAPackageThat(is(annotatedWith(annotationClass))); + } + } + + public static class Conditions { + /** + * Can be removed once haveAnyDependenciesThat is merged. + */ + @Deprecated + public static ArchCondition haveAnyDependenciesThat( + DescribedPredicate predicate) { + return new NebulaAnyDependencyCondition( + "have any dependencies that " + predicate.getDescription(), + predicate, + GET_DIRECT_DEPENDENCIES_FROM_SELF); + } + } +} diff --git a/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaMethod.java b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaMethod.java new file mode 100644 index 0000000..b707284 --- /dev/null +++ b/archrules-common/src/main/java/com/netflix/nebula/archrules/common/JavaMethod.java @@ -0,0 +1,17 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.base.DescribedPredicate; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class JavaMethod { + public static class Predicates { + + /** + * checks if a method is a getter according to JavaBean conventions + */ + public static DescribedPredicate aGetter() { + return new JavaBeanGetterPredicate(); + } + } +} diff --git a/archrules-common/src/main/java/com/tngtech/archunit/lang/conditions/NebulaAnyDependencyCondition.java b/archrules-common/src/main/java/com/tngtech/archunit/lang/conditions/NebulaAnyDependencyCondition.java new file mode 100644 index 0000000..7bfed85 --- /dev/null +++ b/archrules-common/src/main/java/com/tngtech/archunit/lang/conditions/NebulaAnyDependencyCondition.java @@ -0,0 +1,76 @@ +package com.tngtech.archunit.lang.conditions; + + +import com.tngtech.archunit.PublicAPI; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaClass; +import org.jspecify.annotations.NullMarked; + +import java.util.Collection; +import java.util.HashSet; +import java.util.function.Function; + +import static com.tngtech.archunit.PublicAPI.Usage.ACCESS; +import static com.tngtech.archunit.base.DescribedPredicate.alwaysFalse; + +/** + * Duplicate of {@link AnyDependencyCondition}. + * @deprecated Can be removed once haveDependenciesThat is merged. + */ +@Deprecated +@NullMarked +public final class NebulaAnyDependencyCondition extends AnyAttributeMatchesCondition { + private final DescribedPredicate conditionPredicate; + private final Function> javaClassToRelevantDependencies; + private final DescribedPredicate ignorePredicate; + + public NebulaAnyDependencyCondition( + String description, + DescribedPredicate predicate, + Function> javaClassToRelevantDependencies) { + + this(description, predicate, javaClassToRelevantDependencies, alwaysFalse()); + } + + private NebulaAnyDependencyCondition( + String description, + DescribedPredicate conditionPredicate, + Function> javaClassToRelevantDependencies, + DescribedPredicate ignorePredicate) { + + super(description, new DependencyCondition(conditionPredicate)); + this.conditionPredicate = conditionPredicate; + this.javaClassToRelevantDependencies = javaClassToRelevantDependencies; + this.ignorePredicate = ignorePredicate; + } + + @PublicAPI(usage = ACCESS) + public NebulaAnyDependencyCondition ignoreDependency(DescribedPredicate ignorePredicate) { + return new NebulaAnyDependencyCondition(getDescription(), + conditionPredicate, + javaClassToRelevantDependencies, + this.ignorePredicate.or(ignorePredicate)); + } + + @Override + @PublicAPI(usage = ACCESS) + public NebulaAnyDependencyCondition as(String description, Object... args) { + return new NebulaAnyDependencyCondition( + String.format(description, args), + conditionPredicate, + javaClassToRelevantDependencies, + ignorePredicate); + } + + @Override + Collection relevantAttributes(JavaClass javaClass) { + Collection result = new HashSet<>(); + for (Dependency dependency : javaClassToRelevantDependencies.apply(javaClass)) { + if (!ignorePredicate.test(dependency)) { + result.add(dependency); + } + } + return result; + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/CanBeAnnotatedTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/CanBeAnnotatedTest.java new file mode 100644 index 0000000..e48b57c --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/CanBeAnnotatedTest.java @@ -0,0 +1,36 @@ +package com.netflix.nebula.archrules.common; + +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static org.assertj.core.api.Assertions.assertThat; + +class CanBeAnnotatedTest { + + @Test + public void test_javaDeprecatedClass() { + assertThat(CanBeAnnotated.Predicates.deprecated().test(scanClass(Usage.class))).isFalse(); + assertThat(CanBeAnnotated.Predicates.deprecated().getDescription()).isEqualTo("deprecated"); + } + + @Test + public void test_javaDeprecatedForRemovalClass() { + assertThat(CanBeAnnotated.Predicates.deprecatedForRemoval().test(scanClass(Usage.class))).isFalse(); + assertThat(CanBeAnnotated.Predicates.deprecatedForRemoval().getDescription()).isEqualTo("deprecated for removal"); + } + + @Deprecated + static class JavaDeprecatedClass { + + } + + @Deprecated(forRemoval = true) + static class JavaDeprecatedForRemovalClass { + + } + + static class Usage { + JavaDeprecatedClass aField; + JavaDeprecatedForRemovalClass deprecatedForRemoval; + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/DependencyTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/DependencyTest.java new file mode 100644 index 0000000..aa5cf58 --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/DependencyTest.java @@ -0,0 +1,36 @@ +package com.netflix.nebula.archrules.common; + +import com.netflix.nebula.archrules.common.other.ClassInOtherPackage; +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static org.assertj.core.api.Assertions.assertThat; + +public class DependencyTest { + + @Test + public void test_same() { + assertThat(Dependency.Predicates.resideInSamePackage() + .test(scanClass(SameUsage.class).getDirectDependenciesFromSelf().stream() + .filter(d -> d.getTargetClass().isAssignableTo(OtherUsage.class)) + .findFirst().get())) + .isTrue(); + } + + @Test + public void test_other() { + assertThat(Dependency.Predicates.resideInSamePackage() + .test(scanClass(OtherUsage.class).getDirectDependenciesFromSelf().stream() + .filter(d -> d.getTargetClass().isAssignableTo(ClassInOtherPackage.class)) + .findFirst().get())) + .isFalse(); + } + + static class OtherUsage { + ClassInOtherPackage otherPackage; + } + + static class SameUsage { + OtherUsage samePackage; + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAccessTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAccessTest.java new file mode 100644 index 0000000..f9762ab --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAccessTest.java @@ -0,0 +1,39 @@ +package com.netflix.nebula.archrules.common; + +import com.netflix.nebula.archrules.common.other.ClassInOtherPackage; +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static org.assertj.core.api.Assertions.assertThat; + +public class JavaAccessTest { + @Test + public void test_same() { + assertThat(JavaAccess.Predicates.targetHasOwnerInSamePackage() + .test(scanClass(JavaAccessTest.SameUsage.class).getAccessesFromSelf().stream() + .filter(d -> d.getTargetOwner().isAssignableTo(JavaAccessTest.OtherUsage.class)) + .findFirst().get())) + .isTrue(); + } + + @Test + public void test_other() { + assertThat(JavaAccess.Predicates.targetHasOwnerInSamePackage() + .test(scanClass(JavaAccessTest.OtherUsage.class).getAccessesFromSelf().stream() + .filter(d -> d.getTargetOwner().isAssignableTo(ClassInOtherPackage.class)) + .findFirst().get())) + .isFalse(); + } + + static class OtherUsage { + static void usage() { + ClassInOtherPackage.aMethod(); + } + } + + static class SameUsage { + static void usage() { + OtherUsage.usage(); + } + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAnnotationTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAnnotationTest.java new file mode 100644 index 0000000..ca1c50a --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaAnnotationTest.java @@ -0,0 +1,21 @@ +package com.netflix.nebula.archrules.common; + +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static org.assertj.core.api.Assertions.assertThat; + +public class JavaAnnotationTest { + @Test + public void test() { + assertThat(JavaAnnotation.Predicates.deprecatedForRemoval() + .test(scanClass(DeprecatedForRemovalClass.class).getAnnotationOfType("java.lang.Deprecated"))) + .isTrue(); + } + + @Deprecated(forRemoval = true) + static class DeprecatedForRemovalClass { + + } +} + diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaClassTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaClassTest.java new file mode 100644 index 0000000..bb984f8 --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaClassTest.java @@ -0,0 +1,72 @@ +package com.netflix.nebula.archrules.common; + +import com.netflix.nebula.archrules.common.deprecated.ClassInDeprecatedPackage; +import com.netflix.nebula.archrules.common.other.ClassInOtherPackage; +import com.tngtech.archunit.lang.EvaluationResult; +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Dependency.Predicates.resideInSamePackage; +import static com.netflix.nebula.archrules.common.JavaClass.Conditions.haveAnyDependenciesThat; +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static com.netflix.nebula.archrules.common.Util.scanClasses; +import static com.netflix.nebula.archrules.common.Util.scanClassesWithPackage; +import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name; +import static org.assertj.core.api.Assertions.assertThat; + +public class JavaClassTest { + + @Test + public void test_same() { + final var rule = ArchRuleDefinition.noClasses().should(haveAnyDependenciesThat(resideInSamePackage())) + .because("because"); + EvaluationResult result = rule.evaluate(scanClasses(JavaClassTest.SameUsage.class)); + assertThat(result.hasViolation()).isTrue(); + } + + @Test + public void test_other() { + final var rule = ArchRuleDefinition.noClasses().should(haveAnyDependenciesThat(resideInSamePackage())) + .because("because"); + EvaluationResult result = rule.evaluate(scanClasses(JavaClassTest.OtherUsage.class)); + assertThat(result.hasViolation()).isFalse(); + } + + @Test + public void test_resideInPackageAnnotatedWith_true() { + assertThat(JavaClass.Predicates.resideInPackageAnnotatedWith(Deprecated.class) + .test(scanClassesWithPackage(ClassInDeprecatedPackage.class).get(ClassInDeprecatedPackage.class))) + .isTrue(); + } + + @Test + public void test_resideInPackageAnnotatedWith_false() { + assertThat(JavaClass.Predicates.resideInPackageAnnotatedWith(Deprecated.class) + .test(scanClassesWithPackage(ClassInOtherPackage.class).get(ClassInOtherPackage.class))) + .isFalse(); + } + + @Test + public void test_resideInAPackageThat_true() { + assertThat(JavaClass.Predicates.resideInAPackageThat(name("com.netflix.nebula.archrules.common.other")) + .test(scanClass(ClassInOtherPackage.class))) + .isTrue(); + } + + @Test + public void test_resideInAPackageThat_false() { + assertThat(JavaClass.Predicates.resideInAPackageThat(name("com.netflix.nebula.archrules.common")) + .test(scanClass(ClassInOtherPackage.class))) + .isFalse(); + } + + @SuppressWarnings("unused") + static class OtherUsage { + ClassInOtherPackage otherPackage; + } + + @SuppressWarnings("unused") + static class SameUsage { + DependencyTest.OtherUsage samePackage; + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaMethodTest.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaMethodTest.java new file mode 100644 index 0000000..35759e1 --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/JavaMethodTest.java @@ -0,0 +1,33 @@ +package com.netflix.nebula.archrules.common; + +import org.junit.jupiter.api.Test; + +import static com.netflix.nebula.archrules.common.Util.scanClass; +import static org.assertj.core.api.Assertions.assertThat; + +public class JavaMethodTest { + + @Test + public void test_getters_get() { + assertThat(JavaMethod.Predicates.aGetter().test(scanClass(AClass.class).getMethod("get"))) + .isFalse(); + } + + @Test + public void test_getters_getThing() { + assertThat(JavaMethod.Predicates.aGetter().test(scanClass(AClass.class).getMethod("getThing"))) + .isTrue(); + } + + static class AClass { + @SuppressWarnings("unused") + public String get() { + return ""; + } + + @SuppressWarnings("unused") + public String getThing() { + return ""; + } + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/Util.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/Util.java new file mode 100644 index 0000000..be44429 --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/Util.java @@ -0,0 +1,57 @@ +package com.netflix.nebula.archrules.common; + +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.Location; +import com.tngtech.archunit.core.importer.Locations; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +class Util { + private static final ClassFileImporter importer = new ClassFileImporter(); + + static JavaClass scanClass(Class clazz) { + return importer.importClass(clazz); + } + + static JavaClasses scanClasses(Class... classes) { + return importer.importClasses(classes); + } + + /** + * workaround for https://github.com/TNG/ArchUnit/issues/1564 + * @deprecated This is only needed until https://github.com/TNG/ArchUnit/pull/1565 is merged + */ + @Deprecated + static JavaClasses scanClassesWithPackage(Class... classes) { + Set locs = Arrays.stream(classes) + .map(Locations::ofClass) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + List uris = Arrays.stream(classes) + .map(clazz -> clazz.getPackage().getName()) + .map(Locations::ofPackage) + .flatMap(it -> it.stream().map(Location::asURI)) + .map(u -> URI.create(u.toASCIIString() + "package-info.class")) + .map(uri -> { + try { + return uri.toURL(); + } catch (MalformedURLException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + locs.addAll(Locations.of(uris)); + return importer.importLocations(locs); + } +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/ClassInDeprecatedPackage.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/ClassInDeprecatedPackage.java new file mode 100644 index 0000000..f229add --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/ClassInDeprecatedPackage.java @@ -0,0 +1,4 @@ +package com.netflix.nebula.archrules.common.deprecated; + +public class ClassInDeprecatedPackage { +} diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/package-info.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/package-info.java new file mode 100644 index 0000000..b9af894 --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/deprecated/package-info.java @@ -0,0 +1,2 @@ +@Deprecated +package com.netflix.nebula.archrules.common.deprecated; diff --git a/archrules-common/src/test/java/com/netflix/nebula/archrules/common/other/ClassInOtherPackage.java b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/other/ClassInOtherPackage.java new file mode 100644 index 0000000..0e9590c --- /dev/null +++ b/archrules-common/src/test/java/com/netflix/nebula/archrules/common/other/ClassInOtherPackage.java @@ -0,0 +1,7 @@ +package com.netflix.nebula.archrules.common.other; + +public class ClassInOtherPackage { + public static void aMethod() { + + } +} diff --git a/archrules-deprecation/build.gradle.kts b/archrules-deprecation/build.gradle.kts index 372da28..43fd4e9 100644 --- a/archrules-deprecation/build.gradle.kts +++ b/archrules-deprecation/build.gradle.kts @@ -5,6 +5,7 @@ plugins { description = "Arch Rules for detecting usage of deprecated code" dependencies { + archRulesImplementation(project(":archrules-common")) archRulesImplementation(libs.jspecify) archRulesTestImplementation(libs.assertj) diff --git a/archrules-deprecation/gradle.lockfile b/archrules-deprecation/gradle.lockfile index a22781c..ed0e637 100644 --- a/archrules-deprecation/gradle.lockfile +++ b/archrules-deprecation/gradle.lockfile @@ -3,12 +3,12 @@ # This file is expected to be part of source control. ch.qos.logback:logback-classic:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath ch.qos.logback:logback-core:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath -com.netflix.nebula:archrules-joda:0.6.0=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-nullability:0.6.0=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-security:0.6.0=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-testing-frameworks:0.6.0=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-joda:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-nullability:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-security:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-testing-frameworks:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime com.netflix.nebula:nebula-archrules-core:0.5.2=archRules,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:nebula-archrules-core:0.5.5=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +com.netflix.nebula:nebula-archrules-core:0.7.0=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath com.tngtech.archunit:archunit:1.4.1=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime net.bytebuddy:byte-buddy:1.17.7=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=archRulesTestCompileClasspath diff --git a/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java b/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java index 7307714..63b9cc9 100644 --- a/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java +++ b/archrules-deprecation/src/archRules/java/com/netflix/nebula/archrules/deprecation/DeprecationRule.java @@ -1,8 +1,6 @@ package com.netflix.nebula.archrules.deprecation; import com.netflix.nebula.archrules.core.ArchRulesService; -import com.tngtech.archunit.base.DescribedPredicate; -import com.tngtech.archunit.core.domain.JavaAnnotation; import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.Priority; import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; @@ -10,9 +8,17 @@ import java.util.Map; +import static com.netflix.nebula.archrules.common.CanBeAnnotated.Predicates.deprecated; +import static com.netflix.nebula.archrules.common.CanBeAnnotated.Predicates.deprecatedForRemoval; +import static com.netflix.nebula.archrules.common.Dependency.Predicates.resideInSamePackage; +import static com.netflix.nebula.archrules.common.JavaAccess.Predicates.targetHasOwnerInSamePackage; +import static com.netflix.nebula.archrules.common.JavaClass.Conditions.haveAnyDependenciesThat; +import static com.tngtech.archunit.base.DescribedPredicate.doNot; +import static com.tngtech.archunit.base.DescribedPredicate.not; +import static com.tngtech.archunit.core.domain.Dependency.Predicates.dependencyTarget; import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.target; import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.targetOwner; -import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith; +import static com.tngtech.archunit.lang.conditions.ArchPredicates.is; @NullMarked public class DeprecationRule implements ArchRulesService { @@ -20,7 +26,7 @@ public class DeprecationRule implements ArchRulesService { /** * This rule is a stop-gap to find all deprecations. This is likely very noisy, so is a Low priority. * It is recommended to craft more targeted deprecation rules when targeting actual removal of deprecated code. - * + *

* This rule catches: * - Java @Deprecated annotations * - Kotlin @Deprecated annotations @@ -28,43 +34,20 @@ public class DeprecationRule implements ArchRulesService { */ public static final ArchRule deprecationRule = ArchRuleDefinition.priority(Priority.LOW) .noClasses() - // Java deprecated - .should().dependOnClassesThat().areAnnotatedWith(Deprecated.class) - .orShould().accessTargetWhere(targetOwner(annotatedWith(Deprecated.class))) - .orShould().accessTargetWhere(target(annotatedWith(Deprecated.class))) - // Kotlin deprecated - .orShould().dependOnClassesThat().areAnnotatedWith("kotlin.Deprecated") - .orShould().accessTargetWhere(targetOwner(annotatedWith("kotlin.Deprecated"))) - .orShould().accessTargetWhere(target(annotatedWith("kotlin.Deprecated"))) - // Kotlin DeprecatedSinceKotlin - .orShould().dependOnClassesThat().areAnnotatedWith("kotlin.DeprecatedSinceKotlin") - .orShould().accessTargetWhere(targetOwner(annotatedWith("kotlin.DeprecatedSinceKotlin"))) - .orShould().accessTargetWhere(target(annotatedWith("kotlin.DeprecatedSinceKotlin"))) + .should(haveAnyDependenciesThat(doNot(resideInSamePackage()) + .and(dependencyTarget(is(deprecated()))))) + .orShould().accessTargetWhere(not(targetHasOwnerInSamePackage()) + .and(target(is(deprecated())).or(targetOwner(is(deprecated()))))) .allowEmptyShould(true) - .as("No code should reference deprecated APIs (Java or Kotlin)") .because("usage of deprecated APIs introduces risk that future upgrades and migrations will be blocked"); - - private static final DescribedPredicate> deprecatedForRemoval = - new DescribedPredicate>("@Deprecated(forRemoval=true)") { - @Override - public boolean test(JavaAnnotation annotation) { - if (!annotation.getRawType().isEquivalentTo(Deprecated.class)) { - return false; - } - return annotation.get("forRemoval") - .map(value -> (Boolean) value) - .orElse(false); - } - }; - public static final ArchRule deprecationForRemovalRule = ArchRuleDefinition.priority(Priority.MEDIUM) .noClasses() - .should().dependOnClassesThat().areAnnotatedWith(deprecatedForRemoval) - .orShould().accessTargetWhere(targetOwner(annotatedWith(deprecatedForRemoval))) - .orShould().accessTargetWhere(target(annotatedWith(deprecatedForRemoval))) + .should(haveAnyDependenciesThat(doNot(resideInSamePackage()) + .and(dependencyTarget(is(deprecatedForRemoval()))))) + .orShould().accessTargetWhere(not(targetHasOwnerInSamePackage()) + .and(target(is(deprecatedForRemoval())).or(targetOwner(is(deprecatedForRemoval()))))) .allowEmptyShould(true) - .as("No code should reference APIs marked with @Deprecated(forRemoval=true)") .because("these APIs are scheduled for removal and usage will block future upgrades"); @Override diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationForRemovalRuleTest.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationForRemovalRuleTest.java index d860f77..034af76 100644 --- a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationForRemovalRuleTest.java +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationForRemovalRuleTest.java @@ -1,24 +1,31 @@ package com.netflix.nebula.archrules.deprecation; import com.netflix.nebula.archrules.core.Runner; +import com.netflix.nebula.archrules.deprecation.other.DeprecatedForRemovalClass; +import com.netflix.nebula.archrules.deprecation.other.DeprecatedForRemovalMethod; import com.tngtech.archunit.lang.EvaluationResult; import com.tngtech.archunit.lang.Priority; import org.junit.jupiter.api.Test; +import static com.netflix.nebula.archrules.deprecation.DeprecationRuleTest.ACCESS_TARGET_PACKAGE; import static org.assertj.core.api.Assertions.assertThat; public class DeprecationForRemovalRuleTest { + private static final String CLASS_DEPRECATED_FOR_REMOVAL = "have any dependencies that do not reside in same package and target is deprecated for removal"; + private static final String TARGET_IS_DEPRECATED_FOR_REMOVAL = "target is deprecated for removal"; + private static final String TARGET_OWNER_IS_DEPRECATED_FOR_REMOVAL = "target owner is deprecated for removal"; + @Test - public void testDeprecationForRemovalRule_class() { - final EvaluationResult result = Runner.check(DeprecationRule.deprecationForRemovalRule, DeprecationForRemovalRuleTest.CodeThatUsesDeprecatedForRemovalClass.class); + public void testDeprecationForRemovalRule_method() { + final EvaluationResult result = Runner.check(DeprecationRule.deprecationForRemovalRule, CodeThatUsesDeprecatedForRemovalMethod.class); assertThat(result.hasViolation()).isTrue(); assertThat(result.getPriority()).isEqualTo(Priority.MEDIUM); } @Test - public void testDeprecationForRemovalRule_method() { - final EvaluationResult result = Runner.check(DeprecationRule.deprecationForRemovalRule, DeprecationForRemovalRuleTest.CodeThatUsesDeprecatedForRemovalMethod.class); - assertThat(result.hasViolation()).isTrue(); + public void testDeprecationForRemovalRule_method_samePackage() { + final EvaluationResult result = Runner.check(DeprecationRule.deprecationForRemovalRule, DeprecationForRemovalRuleTest.CodeThatUsesDeprecatedForRemovalMethodSamePackage.class); + assertThat(result.hasViolation()).isFalse(); assertThat(result.getPriority()).isEqualTo(Priority.MEDIUM); } @@ -28,12 +35,28 @@ public void testRegularDeprecated() { assertThat(result.hasViolation()).isFalse(); } - @Deprecated(forRemoval = true) - static class DeprecatedForRemovalClass { - static void method() {} + @Test + public void testDeprecatedForRemovalRule_class() { + final EvaluationResult result = Runner.check(DeprecationRule.deprecationForRemovalRule, CodeThatUsesDeprecatedForRemovalClass.class); + assertThat(result.hasViolation()).isTrue(); + assertThat(result.getPriority()).isEqualTo(Priority.MEDIUM); + assertThat(result.getFailureReport().toString()) + .contains("no classes should " + CLASS_DEPRECATED_FOR_REMOVAL + + " or " + ACCESS_TARGET_PACKAGE + " and " + TARGET_IS_DEPRECATED_FOR_REMOVAL + " or "); + + assertThat(result.getFailureReport().toString()) + .as("buggy behavior that will be fixed in https://github.com/TNG/ArchUnit/pull/1579") + .doesNotContain(TARGET_OWNER_IS_DEPRECATED_FOR_REMOVAL); + } + + @Test + public void testDeprecatedForRemovalRule_class_samePackage() { + final EvaluationResult result = Runner.check( + DeprecationRule.deprecationForRemovalRule, CodeThatUsesDeprecatedForRemovalClassSamePackage.class); + assertThat(result.hasViolation()).isFalse(); } - static class DeprecatedForRemovalMethod { + static class DeprecatedForRemovalMethodSamePackage { @Deprecated(forRemoval = true) static void deprecated() {} } @@ -43,15 +66,15 @@ static class RegularDeprecatedClass { static void method() {} } - static class CodeThatUsesDeprecatedForRemovalClass { + static class CodeThatUsesDeprecatedForRemovalMethod { static void usage() { - DeprecatedForRemovalClass.method(); + DeprecatedForRemovalMethod.deprecated(); } } - static class CodeThatUsesDeprecatedForRemovalMethod { + static class CodeThatUsesDeprecatedForRemovalMethodSamePackage { static void usage() { - DeprecatedForRemovalMethod.deprecated(); + DeprecatedForRemovalMethodSamePackage.deprecated(); } } @@ -60,4 +83,16 @@ static void usage() { RegularDeprecatedClass.method(); } } + + @Deprecated(forRemoval = true) + static class DeprecatedForRemovalClassSamePackage { + } + + static class CodeThatUsesDeprecatedForRemovalClass { + DeprecatedForRemovalClass javaDeprecated; + } + + static class CodeThatUsesDeprecatedForRemovalClassSamePackage { + DeprecatedForRemovalClassSamePackage javaDeprecated; + } } diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationRuleTest.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationRuleTest.java index 1db3188..8f41cd3 100644 --- a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationRuleTest.java +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/DeprecationRuleTest.java @@ -1,6 +1,10 @@ package com.netflix.nebula.archrules.deprecation; import com.netflix.nebula.archrules.core.Runner; +import com.netflix.nebula.archrules.deprecation.other.ClassThatIsJavaDeprecated; +import com.netflix.nebula.archrules.deprecation.other.DeprecatedForRemovalClass; +import com.netflix.nebula.archrules.deprecation.other.DeprecatedInterface; +import com.netflix.nebula.archrules.deprecation.other.MethodThatIsDeprecated; import com.tngtech.archunit.lang.EvaluationResult; import com.tngtech.archunit.lang.Priority; import org.junit.jupiter.api.Test; @@ -12,20 +16,47 @@ public class DeprecationRuleTest { private static final Logger LOG = LoggerFactory.getLogger(DeprecationRuleTest.class); + static final String CLASS_DEPRECATED = "have any dependencies that do not reside in same package and target is deprecated"; + static final String ACCESS_TARGET_PACKAGE = "should access target where not in the same package"; + static final String TARGET_IS_DEPRECATED = "target is deprecated"; + static final String TARGET_OWNER_IS_DEPRECATED = "target owner is deprecated"; + @Test public void testDeprecationRule_class() { - final EvaluationResult result = Runner.check(DeprecationRule.deprecationRule, CodeThatUsesDeprecatedClass.class); + final EvaluationResult result = Runner.check(DeprecationRule.deprecationRule, CodeThatUsesJavaDeprecatedClass.class); LOG.info(result.getFailureReport().toString()); assertThat(result.hasViolation()).isTrue(); assertThat(result.getPriority()).isEqualTo(Priority.LOW); + assertThat(result.getFailureReport().toString()) + .contains("no classes should " + CLASS_DEPRECATED + " " + + "or " + ACCESS_TARGET_PACKAGE + " and " + TARGET_IS_DEPRECATED + " or "); + + assertThat(result.getFailureReport().toString()) + .as("buggy behavior that will be fixed in https://github.com/TNG/ArchUnit/pull/1579") + .doesNotContain(TARGET_OWNER_IS_DEPRECATED); } @Test - public void testDeprecationRule_method() { - final EvaluationResult result = Runner.check(DeprecationRule.deprecationRule, CodeThatUsesDeprecatedMethod.class); + public void testDeprecationRule_method_samePackage() { + final EvaluationResult result = + Runner.check(DeprecationRule.deprecationRule, CodeThatUsesDeprecatedMethodSamePackage.class); + assertThat(result.hasViolation()).isFalse(); + } + + @Test + public void testDeprecationRule_method_differentPackage() { + final EvaluationResult result = + Runner.check(DeprecationRule.deprecationRule, CodeThatUsesDeprecatedMethodOtherPackage.class); LOG.info(result.getFailureReport().toString()); assertThat(result.hasViolation()).isTrue(); assertThat(result.getPriority()).isEqualTo(Priority.LOW); + assertThat(result.getFailureReport().toString()) + .contains("no classes should " + CLASS_DEPRECATED + " " + + "or " + ACCESS_TARGET_PACKAGE + " and " + TARGET_IS_DEPRECATED); + + assertThat(result.getFailureReport().toString()) + .as("buggy behavior that will be fixed in https://github.com/TNG/ArchUnit/pull/1579") + .doesNotContain("or target owner is deprecated"); } @Test @@ -60,29 +91,26 @@ static void deprecated() { } } - static class MethodThatIsDeprecated { + static class MethodThatIsDeprecatedSamePackage { @Deprecated static void deprecated() { } } - static class CodeThatUsesDeprecatedMethod { + static class CodeThatUsesDeprecatedMethodSamePackage { static void usage() { - MethodThatIsDeprecated.deprecated(); + MethodThatIsDeprecatedSamePackage.deprecated(); } } - static class CodeThatUsesDeprecatedClass { + static class CodeThatUsesDeprecatedMethodOtherPackage { static void usage() { - ClassIsDeprecated.deprecated(); + MethodThatIsDeprecated.deprecated(); } } - @Deprecated - interface DeprecatedInterface { - void notDeprecated(); - } + static class Impl implements DeprecatedInterface { public void notDeprecated() { @@ -100,4 +128,8 @@ static void usage(Impl impl) { impl.notDeprecated(); } } + + static class CodeThatUsesJavaDeprecatedClass { + ClassThatIsJavaDeprecated javaDeprecated; + } } diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/KotlinDeprecationRuleTest.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/KotlinDeprecationRuleTest.java index 97f662a..c66d54c 100644 --- a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/KotlinDeprecationRuleTest.java +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/KotlinDeprecationRuleTest.java @@ -1,6 +1,8 @@ package com.netflix.nebula.archrules.deprecation; import com.netflix.nebula.archrules.core.Runner; +import com.netflix.nebula.archrules.deprecation.other.DeprecatedSinceKotlinClass; +import com.netflix.nebula.archrules.deprecation.other.KotlinDeprecatedClass; import com.tngtech.archunit.lang.EvaluationResult; import com.tngtech.archunit.lang.Priority; import org.junit.jupiter.api.Test; @@ -28,17 +30,6 @@ public void test_deprecated_since_kotlin_class() { assertThat(result.getPriority()).isEqualTo(Priority.LOW); } - // test helper classes (kotlin deprecated) - @kotlin.Deprecated(message="deprecated") - static class KotlinDeprecatedClass { - static void method() { } - } - - @kotlin.DeprecatedSinceKotlin - static class DeprecatedSinceKotlinClass { - static void method() { } - } - static class CodeThatUsesKotlinDeprecatedClass { static void usage() { KotlinDeprecatedClass.method(); diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsDeprecatedForRemoval.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsDeprecatedForRemoval.java new file mode 100644 index 0000000..44fb458 --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsDeprecatedForRemoval.java @@ -0,0 +1,5 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@Deprecated(forRemoval = true) +public class ClassThatIsDeprecatedForRemoval { +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsJavaDeprecated.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsJavaDeprecated.java new file mode 100644 index 0000000..ca7ac8f --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/ClassThatIsJavaDeprecated.java @@ -0,0 +1,5 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@Deprecated +public class ClassThatIsJavaDeprecated { +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalClass.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalClass.java new file mode 100644 index 0000000..e65cf39 --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalClass.java @@ -0,0 +1,5 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@Deprecated(forRemoval = true) +public class DeprecatedForRemovalClass { +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalMethod.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalMethod.java new file mode 100644 index 0000000..9c6cbf7 --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedForRemovalMethod.java @@ -0,0 +1,6 @@ +package com.netflix.nebula.archrules.deprecation.other; + +public class DeprecatedForRemovalMethod { + @Deprecated(forRemoval = true) + public static void deprecated() {} +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedInterface.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedInterface.java new file mode 100644 index 0000000..7625a2a --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedInterface.java @@ -0,0 +1,6 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@Deprecated +public interface DeprecatedInterface { + void notDeprecated(); +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedSinceKotlinClass.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedSinceKotlinClass.java new file mode 100644 index 0000000..cc94cfb --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/DeprecatedSinceKotlinClass.java @@ -0,0 +1,7 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@kotlin.DeprecatedSinceKotlin +public class DeprecatedSinceKotlinClass { + public static void method() { + } +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/KotlinDeprecatedClass.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/KotlinDeprecatedClass.java new file mode 100644 index 0000000..782657a --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/KotlinDeprecatedClass.java @@ -0,0 +1,7 @@ +package com.netflix.nebula.archrules.deprecation.other; + +@kotlin.Deprecated(message = "deprecated") +public class KotlinDeprecatedClass { + public static void method() { + } +} diff --git a/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/MethodThatIsDeprecated.java b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/MethodThatIsDeprecated.java new file mode 100644 index 0000000..e6625c8 --- /dev/null +++ b/archrules-deprecation/src/archRulesTest/java/com/netflix/nebula/archrules/deprecation/other/MethodThatIsDeprecated.java @@ -0,0 +1,8 @@ +package com.netflix.nebula.archrules.deprecation.other; + +public class MethodThatIsDeprecated { + @Deprecated + public static void deprecated() { + + } +} diff --git a/archrules-gradle-plugin-development/build.gradle.kts b/archrules-gradle-plugin-development/build.gradle.kts index e844f58..1096055 100644 --- a/archrules-gradle-plugin-development/build.gradle.kts +++ b/archrules-gradle-plugin-development/build.gradle.kts @@ -6,6 +6,7 @@ plugins { description = "Arch Rules for detecting bad practices when developing Gradle plugins" dependencies { + archRulesImplementation(project(":archrules-common")) archRulesImplementation(libs.jspecify) archRulesTestImplementation(libs.assertj) diff --git a/archrules-gradle-plugin-development/gradle.lockfile b/archrules-gradle-plugin-development/gradle.lockfile index c9ac4d4..2c0020b 100644 --- a/archrules-gradle-plugin-development/gradle.lockfile +++ b/archrules-gradle-plugin-development/gradle.lockfile @@ -3,18 +3,18 @@ # This file is expected to be part of source control. ch.qos.logback:logback-classic:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath ch.qos.logback:logback-core:1.5.20=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath -com.netflix.nebula:archrules-deprecation:0.6.0=archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-joda:0.6.0=archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-nullability:0.6.0=archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-security:0.6.0=archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:archrules-testing-frameworks:0.6.0=archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:nebula-archrules-core:0.5.2=mainArchRulesRuntime,testArchRulesRuntime -com.netflix.nebula:nebula-archrules-core:0.5.5=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath -com.tngtech.archunit:archunit:1.4.1=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-deprecation:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-joda:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-nullability:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-security:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:archrules-testing-frameworks:0.7.1=archRules,archRulesArchRulesRuntime,archRulesTestArchRulesRuntime,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:nebula-archrules-core:0.5.5=archRules,mainArchRulesRuntime,testArchRulesRuntime +com.netflix.nebula:nebula-archrules-core:0.7.0=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath +com.tngtech.archunit:archunit:1.4.1=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime net.bytebuddy:byte-buddy:1.17.7=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=archRulesTestCompileClasspath org.assertj:assertj-core:3.27.6=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath -org.jspecify:jspecify:1.0.0=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime +org.jspecify:jspecify:1.0.0=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime org.junit.jupiter:junit-jupiter-api:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.12.2=archRulesTestArchRulesRuntime,archRulesTestRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath @@ -24,5 +24,5 @@ org.junit.platform:junit-platform-engine:1.12.2=archRulesTestArchRulesRuntime,ar org.junit.platform:junit-platform-launcher:1.12.2=archRulesTestArchRulesRuntime,archRulesTestRuntimeClasspath org.junit:junit-bom:5.12.2=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath org.opentest4j:opentest4j:1.3.0=archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath -org.slf4j:slf4j-api:2.0.17=archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime -empty=annotationProcessor,archRulesAnnotationProcessor,archRulesTestAnnotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.17=archRules,archRulesArchRulesRuntime,archRulesCompileClasspath,archRulesRuntimeClasspath,archRulesTestArchRulesRuntime,archRulesTestCompileClasspath,archRulesTestRuntimeClasspath,mainArchRulesRuntime,testArchRulesRuntime +empty=annotationProcessor,archRulesAnnotationProcessor,archRulesTestAnnotationProcessor,compileClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradlePluginExtensionProviderApiRule.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradlePluginExtensionProviderApiRule.java index 3a7c8eb..2cc4203 100644 --- a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradlePluginExtensionProviderApiRule.java +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradlePluginExtensionProviderApiRule.java @@ -14,8 +14,8 @@ import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; import org.jspecify.annotations.NullMarked; +import static com.netflix.nebula.archrules.common.JavaMethod.Predicates.aGetter; import static com.netflix.nebula.archrules.gradleplugins.Predicates.fieldWithTypeIn; -import static com.netflix.nebula.archrules.gradleplugins.Predicates.getters; import static com.netflix.nebula.archrules.gradleplugins.Predicates.hasRichPropertyReturnType; import static com.netflix.nebula.archrules.gradleplugins.Predicates.isProviderApiType; import static com.netflix.nebula.archrules.gradleplugins.Predicates.pluginExtensionClass; @@ -86,7 +86,7 @@ public void check(JavaField field, ConditionEvents events) { }; } - static final DescribedPredicate richExtensionPropertyGetters = ArchPredicates.are(getters) + static final DescribedPredicate richExtensionPropertyGetters = ArchPredicates.are(aGetter()) .and(are(hasRichPropertyReturnType)) .and(not(modifier(JavaModifier.PRIVATE))) .and(not(annotatedWith("javax.inject.Inject"))) diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskProviderApiRule.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskProviderApiRule.java index 1140cd9..7067967 100644 --- a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskProviderApiRule.java +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/GradleTaskProviderApiRule.java @@ -18,8 +18,7 @@ import java.util.HashMap; import java.util.Map; -import static com.netflix.nebula.archrules.gradleplugins.Predicates.getters; -import static com.netflix.nebula.archrules.gradleplugins.Predicates.hasInputOutputAnnotation; +import static com.netflix.nebula.archrules.common.JavaMethod.Predicates.aGetter; import static com.netflix.nebula.archrules.gradleplugins.Predicates.hasRichPropertyReturnType; import static com.netflix.nebula.archrules.gradleplugins.Predicates.isProviderApiType; import static com.netflix.nebula.archrules.gradleplugins.TypeConstants.ANNOTATION_INPUT_DIRECTORY; @@ -130,7 +129,7 @@ private void checkMethodForProviderApiUsage(JavaClass taskClass, JavaMethod meth return; } - if (!getters.test(method)) { + if (!aGetter().test(method)) { return; } @@ -197,7 +196,7 @@ private String getRecommendationForType(JavaClass type, boolean isFileAnnotation }; } - static final DescribedPredicate richTaskPropertyGetters = ArchPredicates.are(getters) + static final DescribedPredicate richTaskPropertyGetters = ArchPredicates.are(aGetter()) .and(are(hasRichPropertyReturnType)) .and(not(modifier(PRIVATE))) .and(not(annotatedWith("javax.inject.Inject"))) diff --git a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/Predicates.java b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/Predicates.java index 35b30a5..c2a7b94 100644 --- a/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/Predicates.java +++ b/archrules-gradle-plugin-development/src/archRules/java/com/netflix/nebula/archrules/gradleplugins/Predicates.java @@ -38,37 +38,6 @@ class Predicates { - /** - * Matches getter methods (getX(), isX()) that follow JavaBeans naming conventions. - */ - static final DescribedPredicate getters = new DescribedPredicate("getters") { - @Override - public boolean test(JavaMethod input) { - if (input.getModifiers().contains(JavaModifier.STATIC)) { - return false; - } - if (!input.getParameters().isEmpty()) { - return false; - } - if (input.getName().startsWith("get")) { - if (input.getName().length() < 4 || Character.isLowerCase(input.getName().charAt(3))) { - return false; - } - return true; - } - if (input.getName().startsWith("is")) { - if (input.getRawReturnType().isAssignableTo(Boolean.class)) { - return false; - } - if (Character.isLowerCase(input.getName().charAt(2))) { - return false; - } - return true; - } - return false; - } - }; - /** * Matches methods returning Provider API or FileCollection types. */ diff --git a/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/PredicatesTest.java b/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/PredicatesTest.java index f878301..2c71861 100644 --- a/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/PredicatesTest.java +++ b/archrules-gradle-plugin-development/src/archRulesTest/java/com/netflix/nebula/archrules/gradleplugins/PredicatesTest.java @@ -56,11 +56,6 @@ public void test_annotatedWithFileInputAnnotation() { ).isTrue(); } - @Test - public void test_getters_get() { - assertThat(Predicates.getters.test(scan(AClass.class).getMethod("get"))).isFalse(); - } - static Stream> providerApiTypes() { return Stream.of( Property.class, @@ -139,14 +134,6 @@ public void test_callsMethodOnAny_shouldNotMatchDifferentOwner() { assertThat(matchCount).isEqualTo(0); } - - static class AClass { - @SuppressWarnings("unused") - public String get() { - return ""; - } - } - @SuppressWarnings("unused") static class ClassCallingGetObjects { public void method(Project project) { diff --git a/archrules-security/src/archRules/java/com/netflix/nebula/archrules/security/CveArchRules.java b/archrules-security/src/archRules/java/com/netflix/nebula/archrules/security/CveArchRules.java index 846984b..93c8509 100644 --- a/archrules-security/src/archRules/java/com/netflix/nebula/archrules/security/CveArchRules.java +++ b/archrules-security/src/archRules/java/com/netflix/nebula/archrules/security/CveArchRules.java @@ -25,6 +25,7 @@ public class CveArchRules implements ArchRulesService { "kotlin.io.FilesKt", "createTempFile", "java.lang.String", "java.lang.String", "java.io.File") + .allowEmptyShould(true) .because("A Kotlin application using createTempDir or createTempFile " + "and placing sensitive information within either of these locations " + "would be leaking this information in a read-only way to other users also on this system. " + @@ -37,6 +38,7 @@ public class CveArchRules implements ArchRulesService { .dependOnClassesThat(JavaClass.Predicates.simpleName("FileBackedOutputStream")) .orShould() .accessTargetWhere(targetOwner(assignableTo(JavaClass.Predicates.simpleName("FileBackedOutputStream")))) + .allowEmptyShould(true) .because("CVE-2023-2976: Use of Java's default temporary directory for file creation in " + "`FileBackedOutputStream` in Google Guava versions 1.0 to 31.1 on Unix systems " + "and Android Ice Cream Sandwich allows other users and apps on the machine " + @@ -46,6 +48,7 @@ public class CveArchRules implements ArchRulesService { public static final ArchRule CVE_2020_8908 = ArchRuleDefinition.priority(Priority.HIGH) .noClasses() .should().callMethod("com.google.common.io.Files", "createTempDir") + .allowEmptyShould(true) .because("A temp directory creation vulnerability exists in all versions of Guava, " + "allowing an attacker with access to the machine to potentially access data in a temporary directory " + "created by the Guava API com.google.common.io.Files.createTempDir(). " + @@ -64,6 +67,7 @@ public class CveArchRules implements ArchRulesService { .dependOnClassesThat(JavaClass.Predicates.simpleName("CompoundOrdering")) .orShould() .accessTargetWhere(targetOwner(assignableTo(JavaClass.Predicates.simpleName("CompoundOrdering")))) + .allowEmptyShould(true) .because("Unbounded memory allocation in Google Guava 11.0 through 24.x before 24.1.1 " + "allows remote attackers to conduct denial of service attacks against servers " + "that depend on this library and deserialize attacker-provided data, " + @@ -75,6 +79,7 @@ public class CveArchRules implements ArchRulesService { public static final ArchRule CVE_2024_6763 = ArchRuleDefinition.priority(Priority.HIGH) .noClasses() .should().dependOnClassesThat().haveFullyQualifiedName("org.eclipse.jetty.http.HttpURI") + .allowEmptyShould(true) .because("The HttpURI class does insufficient validation on the authority segment of a URI."); @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index d8d13f1..4a4b890 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ develocity { rootProject.name = "nebula-archrules" +include(":archrules-common") include(":archrules-deprecation") include(":archrules-gradle-plugin-development") include(":archrules-guava")