Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
*.iml
target/
162 changes: 93 additions & 69 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,80 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>9</version>
</parent>

<groupId>fi.solita.clamav</groupId>
<artifactId>clamav-client</artifactId>
<version>1.0.1</version>
<packaging>jar</packaging>
<name>Simple ClamAV client</name>
<description>Simple Java client for using clamd INSTREAM scanning in your application.</description>
<url>https://github.com/solita/clamav-java</url>
<licenses>
<license>
<name>GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1</name>
<url>http://www.gnu.org/licenses/lgpl.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>Antti Virtanen</name>
<email>[email protected]</email>
<organization>Solita</organization>
<organizationUrl>http://www.solita.fi</organizationUrl>
</developer>
</developers>
<groupId>fi.solita.clamav</groupId>
<artifactId>clamav-client</artifactId>
<version>1.0.1</version>
<packaging>jar</packaging>
<name>Simple ClamAV client</name>
<description>Simple Java client for using clamd INSTREAM scanning in your application.</description>
<url>https://github.com/solita/clamav-java</url>
<licenses>
<license>
<name>GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1</name>
<url>http://www.gnu.org/licenses/lgpl.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>Antti Virtanen</name>
<email>[email protected]</email>
<organization>Solita</organization>
<organizationUrl>http://www.solita.fi</organizationUrl>
</developer>
</developers>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<scm>
<connection>scm:git:git://github.com/solita/clamav-java.git</connection>
<developerConnection>scm:git:[email protected]:solita/clamav-java.git</developerConnection>
<url>https://github.com/solita/clamav-java</url>
</scm>
<scm>
<connection>scm:git:git://github.com/solita/clamav-java.git</connection>
<developerConnection>scm:git:[email protected]:solita/clamav-java.git</developerConnection>
<url>https://github.com/solita/clamav-java</url>
</scm>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.14.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.21.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.21.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>

<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<serverId>ossrh-releases-fi.solita</serverId>
<stagingProfileId>ff5044adfb72</stagingProfileId>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<serverId>ossrh-releases-fi.solita</serverId>
<stagingProfileId>ff5044adfb72</stagingProfileId>
</configuration>
</plugin>

</plugins>
</build>
</plugins>
</build>
</project>
2 changes: 1 addition & 1 deletion src/main/java/fi/solita/clamav/ClamAVClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public static boolean isCleanReply(byte[] reply) {
private byte[] assertSizeLimit(byte[] reply) {
String r = new String(reply, StandardCharsets.US_ASCII);
if (r.startsWith("INSTREAM size limit exceeded."))
throw new ClamAVSizeLimitException("Clamd size limit exceeded. Full reply from server: " + r);
throw new ClamAVSizeLimitException("Clamd size limit exceeded. Full reply from server: " + r.trim());
return reply;
}

Expand Down
49 changes: 49 additions & 0 deletions src/test/java/fi/solita/clamav/BaseTestcontainers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package fi.solita.clamav;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.provider.Arguments;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
import org.testcontainers.utility.DockerImageName;

import java.util.stream.Stream;

public abstract class BaseTestcontainers {
public static final ClamavContainer clamav14 = new ClamavContainer("clamav/clamav:1.4");
public static final ClamavContainer clamav15 = new ClamavContainer("clamav/clamav:1.5");

@BeforeAll
static void beforeAll() {
clamav14.start();
clamav15.start();
}

@AfterAll
static void afterAll() {
clamav15.stop();
clamav14.stop();
}

private static Stream<Arguments> provideClamdContainers() {
// INFO: We have to wrap GenericContainer info Supplier wrapper to force JUnit not to close AutoClosable Test method parameter
return Stream.of(
Arguments.of("1.4", clamav14),
Arguments.of("1.5", clamav15)
);
}

protected static ClamAVClient client(GenericContainer<?> container) {
return new ClamAVClient("localhost", container.getFirstMappedPort());
}
}

class ClamavContainer extends GenericContainer<ClamavContainer> {
public static final int DEFAULT_PORT = 3310;

ClamavContainer(String image) {
super(DockerImageName.parse(image));
this.waitingFor(new HostPortWaitStrategy().forPorts(3310));
this.withExposedPorts(3310);
}
}
118 changes: 62 additions & 56 deletions src/test/java/fi/solita/clamav/InstreamTest.java
Original file line number Diff line number Diff line change
@@ -1,66 +1,72 @@
package fi.solita.clamav;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;

import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;

import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* These tests assume clamd is running and responding in the virtual machine.
*/
public class InstreamTest {
public class InstreamTest extends BaseTestcontainers {
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testRandomBytes(String clamdVersion, GenericContainer<?> container) throws IOException {
byte[] r = client(container).scan("alsdklaksdla".getBytes(StandardCharsets.US_ASCII));
assertTrue(ClamAVClient.isCleanReply(r));
}

private static String CLAMAV_HOST = "localhost";

private byte[] scan(byte[] input) throws UnknownHostException, IOException {
ClamAVClient cl = new ClamAVClient(CLAMAV_HOST, 3310);
return cl.scan(input);
}

private byte[] scan(InputStream input) throws UnknownHostException, IOException {
ClamAVClient cl = new ClamAVClient(CLAMAV_HOST, 3310);
return cl.scan(input);
}
@Test
public void testRandomBytes() throws UnknownHostException, IOException {
byte[] r = scan("alsdklaksdla".getBytes("ASCII"));
assertTrue(ClamAVClient.isCleanReply(r));
}

@Test
public void testPositive() throws UnknownHostException, IOException {
// http://www.eicar.org/86-0-Intended-use.html
byte[] EICAR = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".getBytes("ASCII");
byte[] r = scan(EICAR);
assertFalse(ClamAVClient.isCleanReply(r));
}
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testPositive(String clamdVersion, GenericContainer<?> container) throws IOException {
// http://www.eicar.org/86-0-Intended-use.html
byte[] EICAR = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".getBytes(StandardCharsets.US_ASCII);
byte[] r = client(container).scan(EICAR);
assertFalse(ClamAVClient.isCleanReply(r));
}

@Test
public void testStreamChunkingWorks() throws UnknownHostException, IOException {
byte[] multipleChunks = new byte[50000];
byte[] r = scan(multipleChunks);
assertTrue(ClamAVClient.isCleanReply(r));
}

@Test
public void testChunkLimit() throws UnknownHostException, IOException {
byte[] maximumChunk = new byte[2048];
byte[] r = scan(maximumChunk);
assertTrue(ClamAVClient.isCleanReply(r));
}

@Test
public void testZeroBytes() throws UnknownHostException, IOException {
byte[] r = scan(new byte[]{});
assertTrue(ClamAVClient.isCleanReply(r));
}
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testStreamChunkingWorks(String clamdVersion, GenericContainer<?> container) throws IOException {
byte[] multipleChunks = new byte[50000];
byte[] r = client(container).scan(multipleChunks);
assertTrue(ClamAVClient.isCleanReply(r));
}

@Test(expected = ClamAVSizeLimitException.class)
public void testSizeLimit() throws UnknownHostException, IOException {
scan(new SlowInputStream());
}
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testChunkLimit(String clamdVersion, GenericContainer<?> container) throws IOException {
byte[] maximumChunk = new byte[2048];
byte[] r = client(container).scan(maximumChunk);
assertTrue(ClamAVClient.isCleanReply(r));
}

@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testZeroBytes(String clamdVersion, GenericContainer<?> container) throws IOException {
byte[] r = client(container).scan(new byte[]{});
assertTrue(ClamAVClient.isCleanReply(r));
}

@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testSizeLimit(String clamdVersion, GenericContainer<?> container) {
ClamAVSizeLimitException exception = Assertions.assertThrows(ClamAVSizeLimitException.class,
() -> client(container).scan(new SlowInputStream(false)));
assertEquals("Clamd size limit exceeded. Full reply from server: INSTREAM size limit exceeded. ERROR", exception.getMessage());
}

@Disabled
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testSizeLimitSuperSlow(String clamdVersion, GenericContainer<?> container) {
ClamAVSizeLimitException exception = Assertions.assertThrows(ClamAVSizeLimitException.class,
() -> client(container).scan(new SlowInputStream(true)));
assertEquals("Clamd size limit exceeded. Full reply from server: INSTREAM size limit exceeded. ERROR", exception.getMessage());
}
}
24 changes: 11 additions & 13 deletions src/test/java/fi/solita/clamav/PingTest.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package fi.solita.clamav;

import static org.junit.Assert.assertTrue;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;

import java.io.IOException;
import java.net.UnknownHostException;

import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* These tests assume clamd is running and responding in the virtual machine.
*/
public class PingTest {

@Test
public void testPingPong() throws UnknownHostException, IOException {
ClamAVClient cl = new ClamAVClient("localhost", 3310);
assertTrue(cl.ping());
}
public class PingTest extends BaseTestcontainers {
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("provideClamdContainers")
public void testPingPong(String clamdVersion, GenericContainer<?> container) throws IOException {
ClamAVClient client = client(container);
assertTrue(client.ping());
}
}
Loading