Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

smallrye-health as optional dependency #25

Merged
merged 1 commit into from
Feb 3, 2024
Merged
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
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,34 @@ Or add to you pom.xml directly:
Now that you configured your POM to use the service, now you need to configure which scanner(s) you want to use in `application.properties`:

### ClamAV

[ClamAV](https://www.clamav.net/) is an open source Linux based virus scanning engine.
If you don't set a host `quarkus.antivirus.clamav.host` a DevService will start a ClamAV instance for you on a dynamic free port, so you can test locally during development.

```properties
quarkus.antivirus.clamav.enabled=true
quarkus.antivirus.clamav.health.enabled=true
```

#### ClamAV Health Check

If you are using the `quarkus-smallrye-health` extension,
quarkus-vault can add a readiness health check to validate the connection to the ClamAV server.

If enabled (by default) and the extension is present,
when you access the `/q/health/ready` endpoint of your application you will have information about the connection validation status.

You can disable this behavior by setting the property `quarkus.antivirus.clamav.health.enabled` to `false` in your application.properties.

### VirusTotal

[VirusTotal](https://www.virustotal.com/) is a REST API that analyses suspicious files to detect malware using over 70 antivirus scanners. VirusTotal checks the hash of a file to see if it has been scanned and what the results are. You can set the threshold of how many of the 70+ engines you want to report the file as malicious before you consider it a malicious file using the `minimum-votes` property.

```properties
quarkus.antivirus.virustotal.enabled=true
quarkus.antivirus.virustotal.key=<YOUR API KEY>
quarkus.antivirus.virustotal.minimum-votes=1
```

## Usage

Simply inject the `Antivirus` service, and it will run the scan against all configured services. It works against `InputStream` so it can be used in any Quarkus application it is not constrained to REST applications only.
Expand Down
7 changes: 6 additions & 1 deletion deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health-deployment</artifactId>
<artifactId>quarkus-smallrye-health-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
Expand All @@ -49,6 +49,11 @@
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem;

/**
* Main processor for the Antivirus extension.
Expand All @@ -29,12 +30,10 @@ void registerBeans(BuildProducer<AdditionalBeanBuildItem> beans,
.addBeanClasses(VirusTotalEngine.class)
.addBeanClasses(Antivirus.class)
.build());
}

// health check
if (buildConfig.healthEnabled()) {
beans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
.addBeanClasses(ClamAVHealthCheck.class)
.build());
}
@BuildStep
HealthBuildItem addHealthCheck(ClamAVBuildConfig buildConfig) {
return new HealthBuildItem(ClamAVHealthCheck.class.getName(), buildConfig.healthEnabled());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class AntivirusDevModeTest {
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));

@Test
public void writeYourOwnDevModeTest() {
void writeYourOwnDevModeTest() {
// Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information
Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class AntivirusTest {

Expand All @@ -16,8 +17,13 @@ public class AntivirusTest {
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));

@Test
public void writeYourOwnUnitTest() {
void writeYourOwnUnitTest() {
// Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information
Assertions.assertTrue(true, "Add some assertions to " + getClass().getName());
}

@Test
void testHealthServlet() {
RestAssured.when().get("/q/health").then().statusCode(404);
}
}
4 changes: 4 additions & 0 deletions integration-tests/imperative/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.antivirus</groupId>
<artifactId>quarkus-antivirus</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ quarkus.http.limits.max-body-size=200m
quarkus.antivirus.clamav.enabled=true
quarkus.antivirus.clamav.chunk-size=30000
quarkus.antivirus.clamav.devservice.fresh-clam=false
quarkus.antivirus.clamav.health.enabled=true
# VirusTotal
quarkus.antivirus.virustotal.enabled=true
quarkus.antivirus.virustotal.key=<YOUR KEY HERE>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
public class ClamAVResourceTest {

@Test
public void testValidFile() {
void testValidFile() {
given()
.when().get("/clamav/valid")
.then()
Expand All @@ -27,7 +27,7 @@ public void testValidFile() {
}

@Test
public void testInvalidFile() {
void testInvalidFile() {
RestAssured.defaultParser = Parser.TEXT;
Response response = given()
.contentType(ContentType.TEXT)
Expand All @@ -44,4 +44,9 @@ public void testInvalidFile() {
"Scan detected viruses in file 'invalid.txt'! Virus scanner message = stream: Win.Test.EICAR_HDB-1 FOUND"));
}
}

@Test
void testHealthServlet() {
RestAssured.when().get("/q/health").then().statusCode(200);
}
}
1 change: 1 addition & 0 deletions runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ public class ClamAVHealthCheck implements HealthCheck {

@Inject
ClamAVEngine engine;

@Inject
ClamAVRuntimeConfig config;

@Override
public HealthCheckResponse call() {
final String server = String.format("%s:%s", config.host(), config.port());
final String host = config.host().orElseThrow();
final String server = String.format("%s:%s", host, config.port());

HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("ClamAV Daemon");
responseBuilder = engine.ping() ? responseBuilder.up().withData(server, "UP")

responseBuilder = engine.ping()
? responseBuilder.up().withData(server, "UP")
: responseBuilder.down().withData(server, "DOWN");

return responseBuilder.build();
}
}