From 2c112b5307e5fee0b0dbc87a6e34e41fcbf4fec4 Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Wed, 4 Dec 2024 13:25:16 -0500 Subject: [PATCH 1/2] Add strategy and provisional metadata --- .../codetf/CodeTFChangesetEntry.java | 24 +++++++++++++++---- .../java/io/codemodder/codetf/Strategy.java | 12 ++++++++++ .../codemodder/codetf/CodeTFResultTest.java | 13 +++++++++- 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/codemodder/codetf/Strategy.java diff --git a/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java b/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java index a2a8b1c..d6b4aaa 100644 --- a/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java +++ b/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java @@ -15,22 +15,28 @@ public final class CodeTFChangesetEntry { private final List changes; private final CodeTFAiMetadata ai; + private final Strategy strategy; + private final boolean provisional; @JsonCreator public CodeTFChangesetEntry( @JsonProperty("path") final String path, @JsonProperty("diff") final String diff, @JsonProperty("changes") final List changes, - @JsonProperty("ai") final CodeTFAiMetadata ai) { + @JsonProperty("ai") final CodeTFAiMetadata ai, + @JsonProperty("strategy") final Strategy strategy, + @JsonProperty("provisional") final boolean provisional) { this.path = CodeTFValidator.requireRelativePath(path); this.diff = CodeTFValidator.requireNonBlank(diff); this.changes = CodeTFValidator.toImmutableCopyOrEmptyOnNull(changes); this.ai = ai; + this.strategy = strategy; + this.provisional = provisional; } public CodeTFChangesetEntry( final String path, final String diff, final List changes) { - this(path, diff, changes, null); + this(path, diff, changes, null, null, false); } public String getPath() { @@ -53,6 +59,14 @@ public boolean usesAi() { return ai != null; } + public Strategy getStrategy() { + return strategy; + } + + public boolean isProvisional() { + return provisional; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -61,12 +75,14 @@ public boolean equals(Object o) { return Objects.equals(path, entry.path) && Objects.equals(diff, entry.diff) && Objects.equals(changes, entry.changes) - && Objects.equals(ai, entry.ai); + && Objects.equals(ai, entry.ai) + && Objects.equals(strategy, entry.strategy) + && Objects.equals(provisional, entry.provisional); } @Override public int hashCode() { - return Objects.hash(path, diff, changes, ai); + return Objects.hash(path, diff, changes, ai, strategy, provisional); } @Override diff --git a/src/main/java/io/codemodder/codetf/Strategy.java b/src/main/java/io/codemodder/codetf/Strategy.java new file mode 100644 index 0000000..7c88d65 --- /dev/null +++ b/src/main/java/io/codemodder/codetf/Strategy.java @@ -0,0 +1,12 @@ +package io.codemodder.codetf; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum Strategy { + @JsonProperty("ai") + AI, + @JsonProperty("hybrid") + HYBRID, + @JsonProperty("deterministic") + DETERMINISTIC, +} diff --git a/src/test/java/io/codemodder/codetf/CodeTFResultTest.java b/src/test/java/io/codemodder/codetf/CodeTFResultTest.java index b39e1de..ad98945 100644 --- a/src/test/java/io/codemodder/codetf/CodeTFResultTest.java +++ b/src/test/java/io/codemodder/codetf/CodeTFResultTest.java @@ -134,7 +134,8 @@ void it_creates_unfixed_findings() { @Test void it_has_changeset_with_ai() { CodeTFAiMetadata ai = new CodeTFAiMetadata("ai", "best-model-ever", null); - CodeTFChangesetEntry entry = new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai); + CodeTFChangesetEntry entry = + new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, null, false); assertTrue(entry.usesAi()); final var result = @@ -156,6 +157,16 @@ void it_has_changeset_with_ai() { assertTrue(result.usesAi()); } + @Test + void it_has_changeset_with_fix_quality_metadata() { + CodeTFAiMetadata ai = new CodeTFAiMetadata("ai", "best-model-ever", null); + CodeTFChangesetEntry entry = + new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, Strategy.HYBRID, true); + + assertEquals(entry.getStrategy(), Strategy.HYBRID); + assertTrue(entry.isProvisional()); + } + @Test void it_has_failure_state() { Failure state = new Failure("reason", "exception"); From 265ba8a0edc600b3961484b2b4d7908d8ac592e1 Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Wed, 4 Dec 2024 13:43:16 -0500 Subject: [PATCH 2/2] Add fixedFindings to CodeTFChangesetEntry --- .../codetf/CodeTFChangesetEntry.java | 17 +++- .../io/codemodder/codetf/CodeTFResult.java | 4 +- .../codemodder/codetf/CodeTFResultTest.java | 91 ++++++++++++++++++- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java b/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java index d6b4aaa..ed665b1 100644 --- a/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java +++ b/src/main/java/io/codemodder/codetf/CodeTFChangesetEntry.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.Objects; +import java.util.stream.Stream; /** Describes an individual changeset entry. */ public final class CodeTFChangesetEntry { @@ -18,6 +19,8 @@ public final class CodeTFChangesetEntry { private final Strategy strategy; private final boolean provisional; + private final List fixedFindings; + @JsonCreator public CodeTFChangesetEntry( @JsonProperty("path") final String path, @@ -25,18 +28,20 @@ public CodeTFChangesetEntry( @JsonProperty("changes") final List changes, @JsonProperty("ai") final CodeTFAiMetadata ai, @JsonProperty("strategy") final Strategy strategy, - @JsonProperty("provisional") final boolean provisional) { + @JsonProperty("provisional") final boolean provisional, + @JsonProperty("fixedFindings") final List fixedFindings) { this.path = CodeTFValidator.requireRelativePath(path); this.diff = CodeTFValidator.requireNonBlank(diff); this.changes = CodeTFValidator.toImmutableCopyOrEmptyOnNull(changes); this.ai = ai; this.strategy = strategy; this.provisional = provisional; + this.fixedFindings = CodeTFValidator.toImmutableCopyOrEmptyOnNull(fixedFindings); } public CodeTFChangesetEntry( final String path, final String diff, final List changes) { - this(path, diff, changes, null, null, false); + this(path, diff, changes, null, null, false, null); } public String getPath() { @@ -67,6 +72,14 @@ public boolean isProvisional() { return provisional; } + /** Fixed findings that are not associated with any particular change */ + public List getFixedFindings() { + return Stream.concat( + fixedFindings.stream(), + changes.stream().map(CodeTFChange::getFixedFindings).flatMap(List::stream)) + .toList(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/io/codemodder/codetf/CodeTFResult.java b/src/main/java/io/codemodder/codetf/CodeTFResult.java index a32521d..de9ce3a 100644 --- a/src/main/java/io/codemodder/codetf/CodeTFResult.java +++ b/src/main/java/io/codemodder/codetf/CodeTFResult.java @@ -104,9 +104,7 @@ public boolean usesAi() { @JsonIgnore public List getFixedFindings() { return changeset.stream() - .map(CodeTFChangesetEntry::getChanges) - .flatMap(List::stream) - .map(CodeTFChange::getFixedFindings) + .map(CodeTFChangesetEntry::getFixedFindings) .flatMap(List::stream) .collect(Collectors.toList()); } diff --git a/src/test/java/io/codemodder/codetf/CodeTFResultTest.java b/src/test/java/io/codemodder/codetf/CodeTFResultTest.java index ad98945..c3d1b4e 100644 --- a/src/test/java/io/codemodder/codetf/CodeTFResultTest.java +++ b/src/test/java/io/codemodder/codetf/CodeTFResultTest.java @@ -135,7 +135,7 @@ void it_creates_unfixed_findings() { void it_has_changeset_with_ai() { CodeTFAiMetadata ai = new CodeTFAiMetadata("ai", "best-model-ever", null); CodeTFChangesetEntry entry = - new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, null, false); + new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, null, false, null); assertTrue(entry.usesAi()); final var result = @@ -161,7 +161,7 @@ void it_has_changeset_with_ai() { void it_has_changeset_with_fix_quality_metadata() { CodeTFAiMetadata ai = new CodeTFAiMetadata("ai", "best-model-ever", null); CodeTFChangesetEntry entry = - new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, Strategy.HYBRID, true); + new CodeTFChangesetEntry("src/foo", "diff", List.of(), ai, Strategy.HYBRID, true, null); assertEquals(entry.getStrategy(), Strategy.HYBRID); assertTrue(entry.isProvisional()); @@ -173,4 +173,91 @@ void it_has_failure_state() { assertEquals("reason", state.getReason()); assertEquals("exception", state.getException()); } + + @Test + void it_has_changeset_with_change_with_fixed_finding() { + DetectorRule rule = new DetectorRule("rule", "Here's a rule", null); + FixedFinding finding = new FixedFinding("finding", rule); + CodeTFChange change = + new CodeTFChange(1, null, "whatever", CodeTFDiffSide.RIGHT, null, null, List.of(finding)); + CodeTFChangesetEntry entry = new CodeTFChangesetEntry("src/foo", "diff", List.of(change)); + assertEquals(1, entry.getFixedFindings().size()); + + final var result = + new CodeTFResult( + "codemodder:java/deserialization", + "Hardened object deserialization calls against attack", + "Lengthier description about deserialization risks, protections, etc...", + null, + null, + Set.of("/foo/failed.java"), + List.of( + new CodeTFReference( + "https://www.oracle.com/technetwork/java/seccodeguide-139067.html#8", + "Oracle's Secure Coding Guidelines for Java SE")), + null, + List.of(entry), + List.of()); + assertEquals(1, result.getFixedFindings().size()); + } + + @Test + void it_has_changeset_with_fixed_finding() { + DetectorRule rule = new DetectorRule("rule", "Here's a rule", null); + FixedFinding finding = new FixedFinding("finding", rule); + CodeTFChange change = + new CodeTFChange(1, null, "whatever", CodeTFDiffSide.RIGHT, null, null, null); + CodeTFChangesetEntry entry = + new CodeTFChangesetEntry( + "src/foo", "diff", List.of(change), null, null, false, List.of(finding)); + assertEquals(1, entry.getFixedFindings().size()); + + final var result = + new CodeTFResult( + "codemodder:java/deserialization", + "Hardened object deserialization calls against attack", + "Lengthier description about deserialization risks, protections, etc...", + null, + null, + Set.of("/foo/failed.java"), + List.of( + new CodeTFReference( + "https://www.oracle.com/technetwork/java/seccodeguide-139067.html#8", + "Oracle's Secure Coding Guidelines for Java SE")), + null, + List.of(entry), + List.of()); + assertEquals(1, result.getFixedFindings().size()); + } + + @Test + void it_has_multiple_changes_with_findings_and_changeset_with_findings() { + DetectorRule rule = new DetectorRule("rule", "Here's a rule", null); + FixedFinding finding = new FixedFinding("finding", rule); + CodeTFChange change = + new CodeTFChange(1, null, "whatever", CodeTFDiffSide.RIGHT, null, null, List.of(finding)); + CodeTFChange change2 = + new CodeTFChange(1, null, "whatever", CodeTFDiffSide.RIGHT, null, null, List.of(finding)); + CodeTFChangesetEntry entry = + new CodeTFChangesetEntry( + "src/foo", "diff", List.of(change, change2), null, null, false, List.of(finding)); + assertEquals(3, entry.getFixedFindings().size()); + + final var result = + new CodeTFResult( + "codemodder:java/deserialization", + "Hardened object deserialization calls against attack", + "Lengthier description about deserialization risks, protections, etc...", + null, + null, + Set.of("/foo/failed.java"), + List.of( + new CodeTFReference( + "https://www.oracle.com/technetwork/java/seccodeguide-139067.html#8", + "Oracle's Secure Coding Guidelines for Java SE")), + null, + List.of(entry, entry), + List.of()); + assertEquals(6, result.getFixedFindings().size()); + } }