This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 11b2d18cb7 JAMES-4113 RSpamD record details of Virus scan decision
11b2d18cb7 is described below

commit 11b2d18cb75adc59a3f15215023a4b5893387e9a
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Feb 7 22:33:07 2025 +0100

    JAMES-4113 RSpamD record details of Virus scan decision
---
 .../org/apache/james/rspamd/RspamdScanner.java     | 17 +++++------
 .../apache/james/rspamd/model/AnalysisResult.java  | 33 ++++++++++++++--------
 .../rspamd/model/AnalysisResultDeserializer.java   | 14 ++++-----
 .../james/rspamd/client/RspamdHttpClientTest.java  |  6 ++--
 .../model/AnalysisResultDeserializationTest.java   |  2 +-
 5 files changed, 40 insertions(+), 32 deletions(-)

diff --git 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/RspamdScanner.java 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/RspamdScanner.java
index bb81a51410..19a5ce13e1 100644
--- 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/RspamdScanner.java
+++ 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/RspamdScanner.java
@@ -111,23 +111,24 @@ public class RspamdScanner extends GenericMailet {
                 "sender", mail.getMaybeSender().asString(),
                 "recipient", rcptAndResult.getKey().asString(),
                 "rspamDAction", rcptAndResult.getValue().getAction().name(),
+                "virus", rcptAndResult.getValue().getVirusNote().orElse(""),
                 "rspamDRequiredScore", 
Float.toString(rcptAndResult.getValue().getRequiredScore()),
                 "rspamRewrittenSubject", 
rcptAndResult.getValue().getDesiredRewriteSubject().orElse(""),
                 "rspamDScore", 
Float.toString(rcptAndResult.getValue().getScore()))))
             .log("Mail scanned with RSpamD.");
 
         if (AnalysisResult.Action.REJECT == 
rcptAndResult.getValue().getAction()) {
-            rejectSpamProcessor.ifPresent(processor -> processorPerUser(mail, 
rcptAndResult.getKey(), processor));
+            rejectSpamProcessor.ifPresent(processor -> processorPerUser(mail, 
rcptAndResult.getKey(), processor, "Rejected due to high spam score"));
         }
 
         appendRspamdResultHeader(mail, rcptAndResult.getKey(), 
rcptAndResult.getRight());
 
-        if (rcptAndResult.getRight().hasVirus()) {
-            virusProcessor.ifPresent(processor -> processorPerUser(mail, 
rcptAndResult.getKey(), processor));
+        if (rcptAndResult.getRight().getVirusNote().isPresent()) {
+            virusProcessor.ifPresent(processor -> processorPerUser(mail, 
rcptAndResult.getKey(), processor, 
rcptAndResult.getRight().getVirusNote().get()));
         }
     }
 
-    private void processorPerUser(Mail mail, MailAddress rcpt, String 
processor) {
+    private void processorPerUser(Mail mail, MailAddress rcpt, String 
processor, String error) {
         Mail copy = null;
         try {
             copy = mail.duplicate();
@@ -165,12 +166,12 @@ public class RspamdScanner extends GenericMailet {
                 .ifPresent(Throwing.consumer(desiredRewriteSubject -> 
mail.getMessage().setSubject(desiredRewriteSubject)));
         }
 
-        if (rspamdResult.hasVirus()) {
-            virusProcessor.ifPresent(state -> {
+        rspamdResult.getVirusNote()
+            .ifPresent(virusNote -> {
                 LOGGER.info("Detected a mail containing virus. Sending mail {} 
to {}", mail, virusProcessor);
-                mail.setState(state);
+                mail.setErrorMessage(virusNote);
+                virusProcessor.ifPresent(mail::setState);
             });
-        }
     }
 
     private void appendRspamdResultHeader(Mail mail, MailAddress recipient, 
AnalysisResult rspamdResult) {
diff --git 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResult.java
 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResult.java
index 2091207bae..3ded63ca0d 100644
--- 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResult.java
+++ 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResult.java
@@ -23,6 +23,7 @@ import java.util.Objects;
 import java.util.Optional;
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 
@@ -38,10 +39,11 @@ public class AnalysisResult {
         private float score;
         private float requiredScore;
         private Optional<String> desiredRewriteSubject;
-        private boolean hasVirus;
+        private Optional<String> virusNote;
 
         public Builder() {
             desiredRewriteSubject = Optional.empty();
+            virusNote = Optional.empty();
         }
 
         public Builder action(Action action) {
@@ -64,15 +66,24 @@ public class AnalysisResult {
             return this;
         }
 
+        @VisibleForTesting
         public Builder hasVirus(boolean hasVirus) {
-            this.hasVirus = hasVirus;
+            if (hasVirus) {
+                this.virusNote = Optional.of("A notice describing the virus");
+            }
+            return this;
+        }
+
+        @VisibleForTesting
+        public Builder virusNote(String value) {
+            this.virusNote = Optional.of(value);
             return this;
         }
 
         public AnalysisResult build() {
             Preconditions.checkNotNull(action);
 
-            return new AnalysisResult(action, score, requiredScore, 
desiredRewriteSubject, hasVirus);
+            return new AnalysisResult(action, score, requiredScore, 
desiredRewriteSubject, virusNote);
         }
     }
 
@@ -99,14 +110,14 @@ public class AnalysisResult {
     private final float score;
     private final float requiredScore;
     private final Optional<String> desiredRewriteSubject;
-    private final boolean hasVirus;
+    private final Optional<String> virusNote;
 
-    public AnalysisResult(Action action, float score, float requiredScore, 
Optional<String> desiredRewriteSubject, boolean hasVirus) {
+    public AnalysisResult(Action action, float score, float requiredScore, 
Optional<String> desiredRewriteSubject, Optional<String> virusNote) {
         this.action = action;
         this.score = score;
         this.requiredScore = requiredScore;
         this.desiredRewriteSubject = desiredRewriteSubject;
-        this.hasVirus = hasVirus;
+        this.virusNote = virusNote;
     }
 
     public Action getAction() {
@@ -125,8 +136,8 @@ public class AnalysisResult {
         return desiredRewriteSubject;
     }
 
-    public boolean hasVirus() {
-        return hasVirus;
+    public Optional<String> getVirusNote() {
+        return virusNote;
     }
 
     @Override
@@ -138,14 +149,14 @@ public class AnalysisResult {
                 && Objects.equals(this.requiredScore, that.requiredScore)
                 && Objects.equals(this.action, that.action)
                 && Objects.equals(this.desiredRewriteSubject, 
that.desiredRewriteSubject)
-                && Objects.equals(this.hasVirus, that.hasVirus);
+                && Objects.equals(this.virusNote, that.virusNote);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(action, score, requiredScore, 
desiredRewriteSubject, hasVirus);
+        return Objects.hash(action, score, requiredScore, 
desiredRewriteSubject, virusNote);
     }
 
     @Override
@@ -155,7 +166,7 @@ public class AnalysisResult {
             .add("score", score)
             .add("requiredScore", requiredScore)
             .add("desiredRewriteSubject", desiredRewriteSubject)
-            .add("hasVirus", hasVirus)
+            .add("virusNote", virusNote)
             .toString();
     }
 }
diff --git 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResultDeserializer.java
 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResultDeserializer.java
index 25b992826c..a490c90f9e 100644
--- 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResultDeserializer.java
+++ 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/model/AnalysisResultDeserializer.java
@@ -44,9 +44,9 @@ public class AnalysisResultDeserializer extends 
StdDeserializer<AnalysisResult>
         float score = node.get("score").floatValue();
         float requiredScore = node.get("required_score").floatValue();
         Optional<String> desiredRewriteSubject = 
deserializeRewriteSubject(node);
-        boolean hasVirus = deserializeClamVirus(node);
+        Optional<String> virusNote = deserializeClamVirus(node);
 
-        return new AnalysisResult(action,score, requiredScore, 
desiredRewriteSubject, hasVirus);
+        return new AnalysisResult(action,score, requiredScore, 
desiredRewriteSubject, virusNote);
     }
 
     private AnalysisResult.Action deserializeAction(String actionAsString) {
@@ -59,16 +59,12 @@ public class AnalysisResultDeserializer extends 
StdDeserializer<AnalysisResult>
     }
 
     private Optional<String> deserializeRewriteSubject(JsonNode node) {
-        JsonNode rewriteSubjectJsonNode = node.get("subject");
-        if (rewriteSubjectJsonNode == null) {
-            return Optional.empty();
-        }
-        return Optional.of(rewriteSubjectJsonNode.asText());
+        return Optional.ofNullable(node.get("subject")).map(JsonNode::asText);
     }
 
-    private boolean deserializeClamVirus(JsonNode node) {
+    private Optional<String> deserializeClamVirus(JsonNode node) {
         JsonNode clamVirusJsonNode = node.get("symbols").get("CLAM_VIRUS");
-        return clamVirusJsonNode != null;
+        return Optional.ofNullable(clamVirusJsonNode).map(JsonNode::toString);
     }
 
 }
diff --git 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/client/RspamdHttpClientTest.java
 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/client/RspamdHttpClientTest.java
index 964b783e5c..ab63385125 100644
--- 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/client/RspamdHttpClientTest.java
+++ 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/client/RspamdHttpClientTest.java
@@ -158,7 +158,7 @@ class RspamdHttpClientTest {
             
softly.assertThat(analysisResult.getAction()).isEqualTo(AnalysisResult.Action.NO_ACTION);
             
softly.assertThat(analysisResult.getRequiredScore()).isEqualTo(13.5F);
             
softly.assertThat(analysisResult.getDesiredRewriteSubject()).isEqualTo(Optional.empty());
-            softly.assertThat(analysisResult.hasVirus()).isEqualTo(false);
+            softly.assertThat(analysisResult.getVirusNote()).isEmpty();
         });
 
         RequestSpecification rspamdApi = 
WebAdminUtils.spec(Port.of(rspamdExtension.rspamdPort()));
@@ -217,7 +217,7 @@ class RspamdHttpClientTest {
         RspamdHttpClient client = new RspamdHttpClient(configuration);
 
         AnalysisResult analysisResult = client.checkV2(virusMessage).block();
-        assertThat(analysisResult.hasVirus()).isTrue();
+        assertThat(analysisResult.getVirusNote()).isPresent();
     }
 
     @Test
@@ -226,7 +226,7 @@ class RspamdHttpClientTest {
         RspamdHttpClient client = new RspamdHttpClient(configuration);
 
         AnalysisResult analysisResult = 
client.checkV2(nonVirusMessage).block();
-        assertThat(analysisResult.hasVirus()).isFalse();
+        assertThat(analysisResult.getVirusNote()).isEmpty();
     }
 
     @Test
diff --git 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/model/AnalysisResultDeserializationTest.java
 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/model/AnalysisResultDeserializationTest.java
index 52f3abc7f0..3c0b8c63aa 100644
--- 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/model/AnalysisResultDeserializationTest.java
+++ 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/model/AnalysisResultDeserializationTest.java
@@ -227,7 +227,7 @@ public class AnalysisResultDeserializationTest {
             .action(AnalysisResult.Action.NO_ACTION)
             .score(3.5F)
             .requiredScore(14.0F)
-            .hasVirus(true)
+            
.virusNote("{\"name\":\"CLAM_VIRUS\",\"score\":0.0,\"metric_score\":0.0,\"options\":[\"Eicar-Signature\"]}")
             .build());
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to