Skip to content
Snippets Groups Projects
Commit 52ebc696 authored by Martin Lowe's avatar Martin Lowe :flag_ca:
Browse files

Merge branch 'malowe/main/6' into 'main'

Iss #6 - Added Google API support for drive

See merge request !6
parents 0d520e83 7fa94ace
No related branches found
No related tags found
1 merge request!6Iss #6 - Added Google API support for drive
Pipeline #9218 running
Showing
with 771 additions and 562 deletions
...@@ -44,3 +44,4 @@ nb-configuration.xml ...@@ -44,3 +44,4 @@ nb-configuration.xml
/src/test/resources/schemas /src/test/resources/schemas
/.apt_generated/ /.apt_generated/
/.apt_generated_tests/ /.apt_generated_tests/
config/application/client_secrets.json
eclipse.google.jwt-location=$PWD/config/application/client_secrets.json
eclipse.google.cve-file-id=
\ No newline at end of file
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<surefire-plugin.version>3.0.0-M5</surefire-plugin.version> <surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
<auto-value.version>1.8.2</auto-value.version> <auto-value.version>1.8.2</auto-value.version>
<hibernate.version>5.5.6.Final</hibernate.version> <hibernate.version>5.5.6.Final</hibernate.version>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
<eclipse-api-version>0.6.5-SNAPSHOT</eclipse-api-version> <eclipse-api-version>0.6.5-SNAPSHOT</eclipse-api-version>
</properties> </properties>
<repositories> <repositories>
...@@ -79,6 +80,26 @@ ...@@ -79,6 +80,26 @@
<artifactId>commons-text</artifactId> <artifactId>commons-text</artifactId>
<version>1.9</version> <version>1.9</version>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>
<!-- Required for Google Drive harness -->
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
<version>v3-rev20211107-1.32.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
<!-- Annotation preprocessors - reduce all of the boiler plate --> <!-- Annotation preprocessors - reduce all of the boiler plate -->
<dependency> <dependency>
...@@ -96,6 +117,17 @@ ...@@ -96,6 +117,17 @@
<groupId>com.google.code.findbugs</groupId> <groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId> <artifactId>jsr305</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing dependencies only --> <!-- Testing dependencies only -->
<dependency> <dependency>
...@@ -146,6 +178,11 @@ ...@@ -146,6 +178,11 @@
<artifactId>auto-value</artifactId> <artifactId>auto-value</artifactId>
<version>${auto-value.version}</version> <version>${auto-value.version}</version>
</path> </path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
......
/** /**
* Copyright (c) 2022 Eclipse Foundation * Copyright (c) 2022 Eclipse Foundation
* *
* This program and the accompanying materials are made * This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 * available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/ * which is available at https://www.eclipse.org/legal/epl-2.0/
* *
* Author: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org> * Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.cve.api; package org.eclipsefoundation.cve.api;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET; import javax.ws.rs.Consumes;
import javax.ws.rs.Path; import javax.ws.rs.GET;
import javax.ws.rs.PathParam; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.eclipsefoundation.cve.model.CirclCveData; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
/** @ApplicationScoped
* Binding interface for the Circl CVE API. runtime implementations are auromatically generated by @RegisterRestClient
* Quarkus at compile time. @Consumes("application/json")
* public interface GithubCveAPI {
* @author Zachary Sabourin
* @GET
*/ @Path("/{year}/{thousands}xxx/{id}.json")
@ApplicationScoped @Consumes("text/plain")
@RegisterRestClient public Response getCveDetails(@PathParam("year") String year, @PathParam("thousands") String thousands,
@Produces("application/json") @PathParam("id") String cveId);
public interface CveCirclAPI {
}
/**
* Retreives the CVE object that matches the given id
*
* @param id the id to match against CVEs
* @return the matching CirclCveData
*/
@GET
@Path("/api/cve/{id}")
CirclCveData getCveById(@PathParam("id") String id);
}
...@@ -15,11 +15,15 @@ import javax.inject.Inject; ...@@ -15,11 +15,15 @@ import javax.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.eclipsefoundation.core.service.CachingService; import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.cve.api.CveCirclAPI; import org.eclipsefoundation.cve.api.GithubCveAPI;
import org.eclipsefoundation.cve.model.mapper.CveDataMapper;
import org.eclipsefoundation.cve.service.CveSourceService; import org.eclipsefoundation.cve.service.CveSourceService;
import org.eclipsefoundation.cve.service.GoogleAPIService;
import org.eclipsefoundation.cve.service.impl.DefaultCveSourceService; import org.eclipsefoundation.cve.service.impl.DefaultCveSourceService;
import org.eclipsefoundation.cve.service.impl.StubbedCveSourceService; import org.eclipsefoundation.cve.service.impl.StubbedCveSourceService;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.arc.DefaultBean; import io.quarkus.arc.DefaultBean;
import io.quarkus.arc.properties.IfBuildProperty; import io.quarkus.arc.properties.IfBuildProperty;
...@@ -36,20 +40,17 @@ public class CveSourceServiceProvider { ...@@ -36,20 +40,17 @@ public class CveSourceServiceProvider {
@Inject @Inject
@RestClient @RestClient
CveCirclAPI circlApi; GithubCveAPI cveDetailsApi;
@Inject
CachingService cache;
@Produces @Produces
@DefaultBean @DefaultBean
public CveSourceService defaultService() { public CveSourceService defaultService(GoogleAPIService driveApi, CachingService cache, CveDataMapper mapper, ObjectMapper objectMapper) {
return new DefaultCveSourceService(); return new DefaultCveSourceService(driveApi, cveDetailsApi, cache, mapper, objectMapper);
} }
@Produces @Produces
@IfBuildProperty(name = PROVIDER_TYPE_PROPERTY_NAME, stringValue = "stubbed") @IfBuildProperty(name = PROVIDER_TYPE_PROPERTY_NAME, stringValue = "stubbed")
public CveSourceService stubbedService() { public CveSourceService stubbedService() {
return new StubbedCveSourceService(circlApi, cache); return new StubbedCveSourceService();
} }
} }
package org.eclipsefoundation.cve.config;
import java.io.FileInputStream;
import java.util.Collections;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.DataStoreFactory;
import com.google.api.client.util.store.MemoryDataStoreFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import io.quarkus.arc.DefaultBean;
@Dependent
public class GoogleDriveAPIProvider {
@ConfigProperty(name = "eclipse.google.jwt-location")
String location;
@Produces
@DefaultBean
public Drive drive(JsonFactory jsonFactory, HttpTransport httpTransport) {
return new Drive.Builder(httpTransport, jsonFactory, new HttpCredentialsAdapter(authorize()))
.setApplicationName("EclipseFDN CVE reader")
.build();
}
/**
* Retrieve the default JSON factory used for Google API Client connections.
*
* @return default JSON factory implementation used for connections.
*/
@Produces
@DefaultBean
public JsonFactory getJsonFactory() {
return GsonFactory.getDefaultInstance();
}
/**
* Retrieve the default HTTP transport used for Google API Client connections.
*
* @return default HTTP transport implementation used for connections.
*/
@Produces
@DefaultBean
public HttpTransport getHttpTransport() {
return new NetHttpTransport();
}
/**
* Retrieve the default datastore/cache factory used for Google API Client connections.
*
* @return default datastore/cache factory implementation used for connections.
*/
@Produces
@DefaultBean
public DataStoreFactory getDSF() {
return new MemoryDataStoreFactory();
}
/**
* Generate a GoogleCredential to be used in connecting to the Google API client. Uses a stored JWT from the Google
* Cloud console to connect, and sets the scope to reading documents in readonly mode.
*
* @return the credential object used by Google API clients to connect to the services.
* @throws IllegalStateException when there are errors generating the GoogleCredential from the JWT.
*/
private GoogleCredentials authorize() {
// load client secrets
try {
return GoogleCredentials
.fromStream(new FileInputStream(location))
.createScoped(Collections.singleton(DriveScopes.DRIVE_READONLY));
} catch (Exception e) {
throw new IllegalStateException("Unable to load Google API secrets at runtime", e);
}
}
}
package org.eclipsefoundation.cve.model;
import java.util.List;
import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData.Builder.class)
public abstract class CirclCveData {
@JsonProperty("Modified")
public abstract String getModified();
@JsonProperty("Published")
public abstract String getPublished();
public abstract Access getAccess();
public abstract String getAssigner();
@JsonProperty("capec")
public abstract List<Capec> getCapecs();
public abstract Double getCvss();
@JsonProperty("cvss-time")
public abstract String getCvssTime();
@JsonProperty("cvss-vector")
public abstract String getCvssVector();
public abstract String getCwe();
public abstract String getId();
public abstract Impact getImpact();
@JsonProperty("last-modified")
public abstract String getLastModified();
@JsonProperty("msbulletin")
@Nullable
public abstract List<Bulletin> getBulletins();
@JsonProperty("oval")
@Nullable
public abstract List<Oval> getOvals();
public abstract List<String> getReferences();
@JsonProperty("refmap")
@Nullable
public abstract Refmap getRefmap();
@JsonProperty("saint")
@Nullable
public abstract List<Saint> getSaints();
public abstract String getSummary();
@JsonProperty("vulnerable_configuration")
public abstract List<VulnerableConfiguration> getVulnerableConfigurations();
@JsonProperty("vulnerable_configuration_cpe_2_2")
public abstract List<VulnerableConfiguration> getVulnerableConfigurationsCpe2();
@JsonProperty("vulnerable_product")
public abstract List<String> getVulnerableProducts();
public static Builder builder() {
return new AutoValue_CirclCveData.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
@JsonProperty("Modified")
public abstract Builder setModified(String modified);
@JsonProperty("Published")
public abstract Builder setPublished(String published);
public abstract Builder setAccess(Access access);
public abstract Builder setAssigner(String assigner);
@JsonProperty("capec")
public abstract Builder setCapecs(List<Capec> capecs);
public abstract Builder setCvss(Double cvss);
@JsonProperty("cvss-time")
public abstract Builder setCvssTime(String cvssTime);
@JsonProperty("cvss-vector")
public abstract Builder setCvssVector(String cvssVector);
public abstract Builder setCwe(String cwe);
public abstract Builder setId(String id);
public abstract Builder setImpact(Impact impact);
@JsonProperty("last-modified")
public abstract Builder setLastModified(String lastModified);
@JsonProperty("msbulletin")
public abstract Builder setBulletins(@Nullable List<Bulletin> bulletins);
@JsonProperty("oval")
public abstract Builder setOvals(@Nullable List<Oval> ovals);
public abstract Builder setReferences(List<String> references);
public abstract Builder setRefmap(@Nullable Refmap refmap);
@JsonProperty("saint")
public abstract Builder setSaints(@Nullable List<Saint> saints);
public abstract Builder setSummary(String summary);
@JsonProperty("vulnerable_configuration")
public abstract Builder setVulnerableConfigurations(List<VulnerableConfiguration> configurations);
@JsonProperty("vulnerable_configuration_cpe_2_2")
public abstract Builder setVulnerableConfigurationsCpe2(List<VulnerableConfiguration> configurations);
@JsonProperty("vulnerable_product")
public abstract Builder setVulnerableProducts(List<String> vulnerableProducts);
public abstract CirclCveData build();
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Access.Builder.class)
public abstract static class Access {
public abstract String getAuthentication();
public abstract String getComplexity();
public abstract String getVector();
public static Builder builder() {
return new AutoValue_CirclCveData_Access.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setAuthentication(String authentication);
public abstract Builder setComplexity(String complexity);
public abstract Builder setVector(String vector);
public abstract Access build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Capec.Builder.class)
public abstract static class Capec {
public abstract String getId();
public abstract String getName();
public abstract String getPrerequisites();
@JsonProperty("related_weakness")
public abstract List<String> getRelatedWeaknesses();
public abstract String getSolutions();
public abstract String getSummary();
public static Builder builder() {
return new AutoValue_CirclCveData_Capec.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setId(String id);
public abstract Builder setName(String name);
public abstract Builder setPrerequisites(String prerequisites);
@JsonProperty("related_weakness")
public abstract Builder setRelatedWeaknesses(List<String> relatedWeakness);
public abstract Builder setSolutions(String solutions);
public abstract Builder setSummary(String summary);
public abstract Capec build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Impact.Builder.class)
public abstract static class Impact {
public abstract String getAvailability();
public abstract String getConfidentiality();
public abstract String getIntegrity();
public static Builder builder() {
return new AutoValue_CirclCveData_Impact.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setAvailability(String availability);
public abstract Builder setConfidentiality(String confidentiality);
public abstract Builder setIntegrity(String integrity);
public abstract Impact build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Bulletin.Builder.class)
public abstract static class Bulletin {
@JsonProperty("bulletin_id")
public abstract String getBulletinId();
@JsonProperty("bulletin_url")
@Nullable
public abstract String getBulletinUrl();
public abstract String getDate();
public abstract String getImpact();
@JsonProperty("knowledgebase_id")
public abstract String getKnowledgebaseId();
@JsonProperty("knowledgebase_url")
@Nullable
public abstract String getKnowledgebaseUrl();
public abstract String getSeverity();
public abstract String getTitle();
public static Builder builder() {
return new AutoValue_CirclCveData_Bulletin.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setBulletinId(String bulletinId);
public abstract Builder setBulletinUrl(@Nullable String bulletinUrl);
public abstract Builder setDate(String date);
public abstract Builder setImpact(String impact);
public abstract Builder setKnowledgebaseId(String knowledgebaseId);
public abstract Builder setKnowledgebaseUrl(@Nullable String knowledgebaseUrl);
public abstract Builder setSeverity(String severity);
public abstract Builder setTitle(String title);
public abstract Bulletin build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Oval.Builder.class)
public abstract static class Oval {
public abstract String getAccepted();
@JsonProperty("class")
public abstract String getOvalClass();
public abstract List<Contributor> getContributors();
@JsonProperty("definition_extensions")
public abstract List<DefinitionExtension> getDefinitionExtensions();
public abstract String getDescription();
public abstract String getFamily();
public abstract String getId();
public abstract String getStatus();
public abstract String getSubmitted();
public abstract String getTitle();
public abstract String getVersion();
public static Builder builder() {
return new AutoValue_CirclCveData_Oval.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setAccepted(String accepted);
@JsonProperty("class")
public abstract Builder setOvalClass(String ovalClass);
public abstract Builder setContributors(List<Contributor> contributors);
@JsonProperty("definition_extensions")
public abstract Builder setDefinitionExtensions(List<DefinitionExtension> extensions);
public abstract Builder setDescription(String description);
public abstract Builder setFamily(String family);
public abstract Builder setId(String id);
public abstract Builder setStatus(String status);
public abstract Builder setSubmitted(String submitted);
public abstract Builder setTitle(String title);
public abstract Builder setVersion(String version);
public abstract Oval build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Contributor.Builder.class)
public abstract static class Contributor {
public abstract String getName();
public abstract String getOrganization();
public static Builder builder() {
return new AutoValue_CirclCveData_Contributor.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setName(String name);
public abstract Builder setOrganization(String organization);
public abstract Contributor build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_DefinitionExtension.Builder.class)
public abstract static class DefinitionExtension {
public abstract String getComment();
public abstract String getOval();
public static Builder builder() {
return new AutoValue_CirclCveData_DefinitionExtension.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setComment(String comment);
public abstract Builder setOval(String oval);
public abstract DefinitionExtension build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Refmap.Builder.class)
public abstract static class Refmap {
public abstract List<String> getBid();
public abstract List<String> getCert();
public abstract List<String> getIdefense();
public abstract List<String> getSectrack();
public abstract List<String> getSecunia();
public abstract List<String> getSreason();
public abstract List<String> getVupen();
public static Builder builder() {
return new AutoValue_CirclCveData_Refmap.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setBid(List<String> bid);
public abstract Builder setCert(List<String> cert);
public abstract Builder setIdefense(List<String> iDefense);
public abstract Builder setSectrack(List<String> sectrack);
public abstract Builder setSecunia(List<String> secunia);
public abstract Builder setSreason(List<String> sreason);
public abstract Builder setVupen(List<String> vupen);
public abstract Refmap build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_Saint.Builder.class)
public abstract static class Saint {
public abstract String getBid();
public abstract String getDescription();
public abstract String getId();
public abstract String getOsvdb();
public abstract String getTitle();
public abstract String getType();
public static Builder builder() {
return new AutoValue_CirclCveData_Saint.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setBid(String bid);
public abstract Builder setDescription(String description);
public abstract Builder setId(String id);
public abstract Builder setOsvdb(String osvdb);
public abstract Builder setTitle(String title);
public abstract Builder setType(String type);
public abstract Saint build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CirclCveData_VulnerableConfiguration.Builder.class)
public abstract static class VulnerableConfiguration {
public abstract String getId();
public abstract String getTitle();
public static Builder builder() {
return new AutoValue_CirclCveData_VulnerableConfiguration.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setId(String id);
public abstract Builder setTitle(String title);
public abstract VulnerableConfiguration build();
}
}
}
/*******************************************************************************
* Copyright (C) 2022 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipsefoundation.cve.model;
import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/**
* CSV model class for CVE data. Cannot be autovalue as it doesn't work well with Jackson CSV mapping.
*
* @author Martin Lowe
*
*/
@JsonPropertyOrder({ "ID", "Date", "Project", "Link", "Request Link", "CVE Pull Request", "Live Link", "Status", "Year",
"Top Level Project" })
public class CveCSVData {
@JsonProperty("ID")
private String id;
@JsonProperty("Date")
private String date;
@JsonProperty("Project")
private String project;
@JsonProperty("Link")
private String link;
@JsonProperty("Request Link")
private String requestLink;
@JsonProperty("CVE Pull Request")
private String cvePullRequest;
@JsonProperty("Live Link")
private String liveLink;
@JsonProperty("Status")
private String status;
@JsonProperty("Year")
private String year;
@JsonProperty("Top Level Project")
private String topLevelProject;
@Nullable
public String getId() {
return this.id;
}
public String getDate() {
return this.date;
}
@Nullable
public String getProject() {
return this.project;
}
@Nullable
public String getLink() {
return this.link;
}
public String getRequestLink() {
return this.requestLink;
}
@Nullable
public String getCvePullRequest() {
return this.cvePullRequest;
}
public String getLiveLink() {
return this.liveLink;
}
public String getStatus() {
return this.status;
}
public String getYear() {
return this.year;
}
public String getTopLevelProject() {
return this.topLevelProject;
}
public void setId(String id) {
this.id = id;
}
public void setDate(String date) {
this.date = date;
}
public void setProject(String project) {
this.project = project;
}
public void setLink(String link) {
this.link = link;
}
public void setRequestLink(String requestLink) {
this.requestLink = requestLink;
}
public void setCvePullRequest(String cvePullRequest) {
this.cvePullRequest = cvePullRequest;
}
public void setLiveLink(String liveLink) {
this.liveLink = liveLink;
}
public void setStatus(String status) {
this.status = status;
}
public void setYear(String year) {
this.year = year;
}
public void setTopLevelProject(String topLevelProject) {
this.topLevelProject = topLevelProject;
}
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
******************************************************************************/ ******************************************************************************/
package org.eclipsefoundation.cve.model; package org.eclipsefoundation.cve.model;
import java.time.ZonedDateTime; import java.util.Date;
import javax.annotation.Nullable; import javax.annotation.Nullable;
...@@ -26,12 +26,15 @@ import com.google.auto.value.AutoValue; ...@@ -26,12 +26,15 @@ import com.google.auto.value.AutoValue;
@AutoValue @AutoValue
@JsonDeserialize(builder = AutoValue_CveData.Builder.class) @JsonDeserialize(builder = AutoValue_CveData.Builder.class)
public abstract class CveData { public abstract class CveData {
@Nullable
public abstract String getId(); public abstract String getId();
public abstract ZonedDateTime getDate(); public abstract Date getDate();
@Nullable
public abstract String getProject(); public abstract String getProject();
@Nullable
public abstract String getLink(); public abstract String getLink();
public abstract String getRequestLink(); public abstract String getRequestLink();
...@@ -74,13 +77,13 @@ public abstract class CveData { ...@@ -74,13 +77,13 @@ public abstract class CveData {
@AutoValue.Builder @AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set") @JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder { public abstract static class Builder {
public abstract Builder setId(String id); public abstract Builder setId(@Nullable String id);
public abstract Builder setDate(ZonedDateTime date); public abstract Builder setDate(Date date);
public abstract Builder setProject(String project); public abstract Builder setProject(@Nullable String project);
public abstract Builder setLink(String link); public abstract Builder setLink(@Nullable String link);
public abstract Builder setRequestLink(String requestLink); public abstract Builder setRequestLink(String requestLink);
......
/**
* Copyright (c) 2022 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.cve.model;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
@AutoValue
@JsonDeserialize(builder = AutoValue_CveProjectData.Builder.class)
public abstract class CveProjectData {
public abstract Description getDescription();
public abstract Optional<Impact> getImpact();
public static Builder builder() {
return new AutoValue_CveProjectData.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setDescription(Description description);
public abstract Builder setImpact(Optional<Impact> impact);
public abstract CveProjectData build();
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CveProjectData_Impact.Builder.class)
public abstract static class Impact {
public abstract ImpactScore getCvss();
public static Builder builder() {
return new AutoValue_CveProjectData_Impact.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setCvss(ImpactScore cvss);
public abstract Impact build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CveProjectData_ImpactScore.Builder.class)
public abstract static class ImpactScore {
@Nullable
public abstract Double getBaseScore();
@Nullable
public abstract String getVectorString();
public abstract String getVersion();
public static Builder builder() {
return new AutoValue_CveProjectData_ImpactScore.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setBaseScore(@Nullable Double baseScore);
public abstract Builder setVectorString(@Nullable String vectorString);
public abstract Builder setVersion(String version);
public abstract ImpactScore build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CveProjectData_Description.Builder.class)
public abstract static class Description {
public abstract List<LocalizedValue> getDescriptionData();
public static Builder builder() {
return new AutoValue_CveProjectData_Description.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setDescriptionData(List<LocalizedValue> descriptionData);
public abstract Description build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CveProjectData_LocalizedValue.Builder.class)
public abstract static class LocalizedValue {
public abstract String getLang();
public abstract String getValue();
public static Builder builder() {
return new AutoValue_CveProjectData_LocalizedValue.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setLang(String lang);
public abstract Builder setValue(String value);
public abstract LocalizedValue build();
}
}
}
package org.eclipsefoundation.cve.model.mapper;
import org.eclipsefoundation.cve.model.CveCSVData;
import org.eclipsefoundation.cve.model.CveData;
import org.eclipsefoundation.cve.model.mapper.CveDataMapper.QuarkusMappingConfig;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
@Mapper(config = QuarkusMappingConfig.class)
public interface CveDataMapper{
@Mapping(target = "date", dateFormat = "yyyy-MM-dd")
CveData toJsonModel(CveCSVData csvData);
@InheritInverseConfiguration
CveCSVData toCSVModel(CveData jsonData);
@MapperConfig(componentModel = "cdi")
public static interface QuarkusMappingConfig {
}
}
...@@ -29,6 +29,7 @@ import org.apache.commons.lang3.StringUtils; ...@@ -29,6 +29,7 @@ import org.apache.commons.lang3.StringUtils;
import org.eclipsefoundation.cve.model.CveData; import org.eclipsefoundation.cve.model.CveData;
import org.eclipsefoundation.cve.namespace.CveUrlParameterNames; import org.eclipsefoundation.cve.namespace.CveUrlParameterNames;
import org.eclipsefoundation.cve.service.CveSourceService; import org.eclipsefoundation.cve.service.CveSourceService;
import org.eclipsefoundation.cve.service.GoogleAPIService;
/** /**
* Retrieves working group definitions from the working groups service. * Retrieves working group definitions from the working groups service.
...@@ -42,6 +43,8 @@ public class CveResource { ...@@ -42,6 +43,8 @@ public class CveResource {
@Inject @Inject
CveSourceService cveSource; CveSourceService cveSource;
@Inject
GoogleAPIService api;
@GET @GET
public Response get(@QueryParam(CveUrlParameterNames.PROJECT_NAME_PARAM_NAME) String projectName) { public Response get(@QueryParam(CveUrlParameterNames.PROJECT_NAME_PARAM_NAME) String projectName) {
...@@ -60,7 +63,7 @@ public class CveResource { ...@@ -60,7 +63,7 @@ public class CveResource {
Optional<CveData> cve = cveSource.getCve(id); Optional<CveData> cve = cveSource.getCve(id);
// Returns 404 if status isn't "completed" // Returns 404 if status isn't "completed"
if (cve.isEmpty() || !cve.get().getStatus().equalsIgnoreCase("completed")) { if (cve.isEmpty() || !cve.get().getStatus().equalsIgnoreCase("complete")) {
return Response.status(404).build(); return Response.status(404).build();
} }
......
...@@ -14,8 +14,12 @@ import java.util.Optional; ...@@ -14,8 +14,12 @@ import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.eclipsefoundation.cve.model.CirclCveData;
import org.eclipsefoundation.cve.model.CveData; import org.eclipsefoundation.cve.model.CveData;
import org.eclipsefoundation.cve.model.CveProjectData;
import org.eclipsefoundation.cve.model.CveProjectData.LocalizedValue;
import org.jboss.resteasy.client.exception.ResteasyWebApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Service for retrieving and augmenting CVE data for return in the API. * Service for retrieving and augmenting CVE data for return in the API.
...@@ -24,6 +28,7 @@ import org.eclipsefoundation.cve.model.CveData; ...@@ -24,6 +28,7 @@ import org.eclipsefoundation.cve.model.CveData;
* *
*/ */
public interface CveSourceService { public interface CveSourceService {
public static final Logger LOGGER = LoggerFactory.getLogger(CveSourceService.class);
/** /**
* Gets all CVE data objects, including ones that are not considered completed/public knowledge. * Gets all CVE data objects, including ones that are not considered completed/public knowledge.
...@@ -38,7 +43,7 @@ public interface CveSourceService { ...@@ -38,7 +43,7 @@ public interface CveSourceService {
* @param id the id to match against CVEs. * @param id the id to match against CVEs.
* @return the Circl CVE data or empty optional if it can't be found. * @return the Circl CVE data or empty optional if it can't be found.
*/ */
Optional<CirclCveData> getCirclCve(String id); Optional<CveProjectData> getCveDetails(String id);
/** /**
* Retrieves a list of all public CVEs. Any non-public CVEs will be filtered out for security. * Retrieves a list of all public CVEs. Any non-public CVEs will be filtered out for security.
...@@ -46,7 +51,7 @@ public interface CveSourceService { ...@@ -46,7 +51,7 @@ public interface CveSourceService {
* @return all public CVE entries. * @return all public CVE entries.
*/ */
default List<CveData> getPublicCves() { default List<CveData> getPublicCves() {
return getCves(false).collect(Collectors.toList()); return getCves(false).map(cve -> augmentCveData(cve)).collect(Collectors.toList());
} }
/** /**
...@@ -57,7 +62,10 @@ public interface CveSourceService { ...@@ -57,7 +62,10 @@ public interface CveSourceService {
* @return list of CVE entries that match the given filters. * @return list of CVE entries that match the given filters.
*/ */
default List<CveData> getForProject(String projectName, boolean includeInternal) { default List<CveData> getForProject(String projectName, boolean includeInternal) {
return getCves(includeInternal).filter(cve -> cve.getProject().equalsIgnoreCase(projectName)).collect(Collectors.toList()); return getCves(includeInternal)
.filter(cve -> cve.getProject().equalsIgnoreCase(projectName))
.map(this::augmentCveData)
.collect(Collectors.toList());
} }
/** /**
...@@ -67,7 +75,7 @@ public interface CveSourceService { ...@@ -67,7 +75,7 @@ public interface CveSourceService {
* @return CveData object or null. * @return CveData object or null.
*/ */
default Optional<CveData> getCve(String id) { default Optional<CveData> getCve(String id) {
return getCves(false).filter(cve -> cve.getId().equalsIgnoreCase(id)).map(cve -> augmentCveData(cve)).findFirst(); return getCves(false).filter(cve -> cve.getId().equalsIgnoreCase(id)).map(this::augmentCveData).findFirst();
} }
/** /**
...@@ -78,7 +86,7 @@ public interface CveSourceService { ...@@ -78,7 +86,7 @@ public interface CveSourceService {
* @return stream of CVEs filtered on the status. * @return stream of CVEs filtered on the status.
*/ */
default Stream<CveData> getCves(boolean includeInternal) { default Stream<CveData> getCves(boolean includeInternal) {
return getAllCves().stream().filter(cve -> cve.getStatus().equalsIgnoreCase("completed") || includeInternal); return getAllCves().stream().filter(cve -> cve.getStatus().equalsIgnoreCase("complete") || includeInternal);
} }
/** /**
...@@ -88,9 +96,26 @@ public interface CveSourceService { ...@@ -88,9 +96,26 @@ public interface CveSourceService {
* @return CirclCveData object or null. * @return CirclCveData object or null.
*/ */
default CveData augmentCveData(CveData orig) { default CveData augmentCveData(CveData orig) {
Optional<CirclCveData> circlCve = getCirclCve(orig.getId()); try {
if (circlCve.isPresent()) { Optional<CveProjectData> cveDetails = getCveDetails(orig.getId());
return CveData.copy(orig).setCirclSummary(circlCve.get().getSummary()).setCirclCvss(circlCve.get().getCvss()).build(); if (cveDetails.isPresent()) {
return CveData
.copy(orig)
.setCirclSummary(cveDetails
.get()
.getDescription()
.getDescriptionData()
.stream()
.filter(dd -> dd.getLang().equalsIgnoreCase("en"))
.findFirst()
.orElse(LocalizedValue.builder().setLang("en").setValue("").build())
.getValue())
.setCirclCvss(cveDetails.get().getImpact().isPresent() ? cveDetails.get().getImpact().get().getCvss().getBaseScore()
: null)
.build();
}
} catch (ResteasyWebApplicationException e) {
LOGGER.error("Error retrieving CVE detailed data:", e);
} }
return orig; return orig;
} }
......
/**
* Copyright (c) 2022 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.cve.service;
import java.util.List;
import org.eclipsefoundation.cve.model.CveCSVData;
/**
* Service binding for interacting with GoogleAPI data, and authenticating those requests.
*/
public interface GoogleAPIService {
/**
* Retrieves the CVE data needed for application from the GoogleDrive
*
* @return the CVE data for Eclipse Foundation.
*/
List<CveCSVData> getCveSource();
}
/******************************************************************************* /**
* Copyright (C) 2022 Eclipse Foundation * Copyright (c) 2022 Eclipse Foundation
* *
* This program and the accompanying materials are made * This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 * available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/ * which is available at https://www.eclipse.org/legal/epl-2.0/
* *
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
******************************************************************************/ */
package org.eclipsefoundation.cve.service.impl; package org.eclipsefoundation.cve.service.impl;
import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
import javax.ws.rs.core.Response;
import org.eclipsefoundation.cve.model.CirclCveData; import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.cve.api.GithubCveAPI;
import org.eclipsefoundation.cve.model.CveData; import org.eclipsefoundation.cve.model.CveData;
import org.eclipsefoundation.cve.model.CveProjectData;
import org.eclipsefoundation.cve.model.mapper.CveDataMapper;
import org.eclipsefoundation.cve.service.CveSourceService; import org.eclipsefoundation.cve.service.CveSourceService;
import org.eclipsefoundation.cve.service.GoogleAPIService;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
/** /**
* Default implementation of the CVE Service * Default implementation of the CVE Service
...@@ -24,15 +41,46 @@ import org.eclipsefoundation.cve.service.CveSourceService; ...@@ -24,15 +41,46 @@ import org.eclipsefoundation.cve.service.CveSourceService;
* *
*/ */
public class DefaultCveSourceService implements CveSourceService { public class DefaultCveSourceService implements CveSourceService {
public static final Pattern CVE_ID_PARTS = Pattern.compile("^CVE-(\\d{4})-(\\d+?)\\d{3}$");
private GoogleAPIService api;
private GithubCveAPI cveDetailsApi;
private CachingService cache;
private CveDataMapper mapper;
private ObjectMapper om;
/**
* Constructor that loads in all of the external services required to retrieve CVE data.
*/
public DefaultCveSourceService(GoogleAPIService api, GithubCveAPI cveDetailsApi, CachingService cache, CveDataMapper mapper,
ObjectMapper om) {
this.api = api;
this.mapper = mapper;
this.cveDetailsApi = cveDetailsApi;
this.cache = cache;
this.om = om;
}
@Override @Override
public List<CveData> getAllCves() { public List<CveData> getAllCves() {
return Collections.emptyList(); return cache
.get("all", new MultivaluedMapImpl<>(), CveData.class,
() -> api.getCveSource().stream().map(mapper::toJsonModel).collect(Collectors.toList()))
.orElse(Collections.emptyList());
} }
@Override @Override
public Optional<CirclCveData> getCirclCve(String id) { public Optional<CveProjectData> getCveDetails(String id) {
return Optional.empty(); Matcher m = CVE_ID_PARTS.matcher(id);
if (!m.matches()) {
LOGGER.warn("CVE passed with '{}' could not be parsed as a valid CVE ID, returning empty data", id);
return Optional.empty();
}
// get the cached data
return cache.get(id, new MultivaluedMapImpl<>(), CveProjectData.class, () -> {
Response r = cveDetailsApi.getCveDetails(m.group(1), m.group(2), id);
Object responseBody = r.getEntity();
return om.readerFor(CveProjectData.class).readValue(new GZIPInputStream((InputStream) responseBody));
});
} }
} }
/**
* Copyright (c) 2022 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.cve.service.impl;
import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.cve.model.CveCSVData;
import org.eclipsefoundation.cve.service.GoogleAPIService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.api.services.drive.Drive;
/**
* Default implementation of the {@linkplain GoogleAPIService}. Connects to the server via a stored JWT and retrieves
* CVE source information from a set Google Drive CSV file.
*
* @author Martin Lowe
*/
@ApplicationScoped
public class DefaultGoogleAPIService implements GoogleAPIService {
public static final Logger LOGGER = LoggerFactory.getLogger(DefaultGoogleAPIService.class);
@ConfigProperty(name = "eclipse.google.cve-file-id")
String cveFileId;
@Inject
Drive drive;
// used to read in CSV data
final CsvMapper CSV_MAPPER = (CsvMapper) new CsvMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
@Override
public List<CveCSVData> getCveSource() {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
// retrieve the CSV file from Google Drive
drive.files().export(cveFileId, "text/csv").executeAndDownloadTo(baos);
List<Object> out = CSV_MAPPER
.readerFor(CveCSVData.class)
.with(CSV_MAPPER.schemaFor(CveCSVData.class).withHeader().withColumnSeparator(',').withNullValue(""))
.readValues(new String(baos.toByteArray()))
.readAll();
// check that our data returned in proper format
if (out.isEmpty() || !(out.get(0) instanceof CveCSVData)) {
return Collections.emptyList();
}
// can't directly cast list, so wrap a stream to check
return out.stream().map(o -> (CveCSVData) o).collect(Collectors.toList());
} catch (Exception e) {
LOGGER.error("Error while reading in data from API", e);
}
return null;
}
}
...@@ -14,16 +14,13 @@ package org.eclipsefoundation.cve.service.impl; ...@@ -14,16 +14,13 @@ package org.eclipsefoundation.cve.service.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.eclipsefoundation.core.helper.DateTimeHelper;
import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.cve.api.CveCirclAPI;
import org.eclipsefoundation.cve.model.CirclCveData;
import org.eclipsefoundation.cve.model.CveData; import org.eclipsefoundation.cve.model.CveData;
import org.eclipsefoundation.cve.model.CveProjectData;
import org.eclipsefoundation.cve.service.CveSourceService; import org.eclipsefoundation.cve.service.CveSourceService;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
/** /**
* Stubbed implementation of CVE data source service. This will provide faked and consistent CVE data for demo/testing * Stubbed implementation of CVE data source service. This will provide faked and consistent CVE data for demo/testing
...@@ -34,24 +31,20 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl; ...@@ -34,24 +31,20 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
*/ */
public class StubbedCveSourceService implements CveSourceService { public class StubbedCveSourceService implements CveSourceService {
private List<CveData> internal; private List<CveData> internal;
private CveCirclAPI api;
private CachingService cache;
public StubbedCveSourceService(CveCirclAPI api, CachingService cache) { public StubbedCveSourceService() {
this.api = api;
this.cache = cache;
this.internal = new ArrayList<>(); this.internal = new ArrayList<>();
this.internal this.internal
.addAll(Arrays .addAll(Arrays
.asList(CveData .asList(CveData
.builder() .builder()
.setId("CVE-2020-27225") .setId("CVE-2020-27225")
.setDate(DateTimeHelper.now()) .setDate(new Date())
.setLink("https://www.eclipse.org/projects/tools/report.php?id=eclipse.platform") .setLink("https://www.eclipse.org/projects/tools/report.php?id=eclipse.platform")
.setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27225") .setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27225")
.setProject("eclipse.platform") .setProject("eclipse.platform")
.setRequestLink("https://bugs.eclipse.org/bugs/show_bug.cgi?id=569855") .setRequestLink("https://bugs.eclipse.org/bugs/show_bug.cgi?id=569855")
.setStatus("Completed") .setStatus("Complete")
.setTopLevelProject("eclipse") .setTopLevelProject("eclipse")
.setCvePullRequest("https://github.com/CVEProject/cvelist/pull/1012") .setCvePullRequest("https://github.com/CVEProject/cvelist/pull/1012")
.setYear(2021) .setYear(2021)
...@@ -59,12 +52,12 @@ public class StubbedCveSourceService implements CveSourceService { ...@@ -59,12 +52,12 @@ public class StubbedCveSourceService implements CveSourceService {
CveData CveData
.builder() .builder()
.setId("CVE-2022-0103") .setId("CVE-2022-0103")
.setDate(DateTimeHelper.now()) .setDate(new Date())
.setLink("https://www.eclipse.org/projects/tools/report.php?id=technology.dash") .setLink("https://www.eclipse.org/projects/tools/report.php?id=technology.dash")
.setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649") .setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649")
.setProject("technology.dash") .setProject("technology.dash")
.setRequestLink("https://bugs.eclipse.org/bugs/show_bug.cgi?id=1") .setRequestLink("https://bugs.eclipse.org/bugs/show_bug.cgi?id=1")
.setStatus("Completed") .setStatus("Complete")
.setTopLevelProject("technology") .setTopLevelProject("technology")
.setCvePullRequest(null) .setCvePullRequest(null)
.setYear(2022) .setYear(2022)
...@@ -72,7 +65,7 @@ public class StubbedCveSourceService implements CveSourceService { ...@@ -72,7 +65,7 @@ public class StubbedCveSourceService implements CveSourceService {
CveData CveData
.builder() .builder()
.setId("CVE-2022-0104") .setId("CVE-2022-0104")
.setDate(DateTimeHelper.now()) .setDate(new Date())
.setLink("https://www.eclipse.org/projects/tools/report.php?id=technology.dash") .setLink("https://www.eclipse.org/projects/tools/report.php?id=technology.dash")
.setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649") .setLiveLink("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649")
.setProject("technology.dash") .setProject("technology.dash")
...@@ -89,14 +82,8 @@ public class StubbedCveSourceService implements CveSourceService { ...@@ -89,14 +82,8 @@ public class StubbedCveSourceService implements CveSourceService {
return new ArrayList<>(this.internal); return new ArrayList<>(this.internal);
} }
/**
* Checks the cache for a CirclCve with a matching id. Hits the Circl API if no CVE found in cache.
*
* @param id the id to match against CVEs.
* @return CirclCveData object or null.
*/
@Override @Override
public Optional<CirclCveData> getCirclCve(String id) { public Optional<CveProjectData> getCveDetails(String id) {
return cache.get(id, new MultivaluedMapImpl<>(), CirclCveData.class, () -> api.getCveById(id)); return Optional.empty();
} }
} }
quarkus.rest-client."org.eclipsefoundation.cve.api.CveCirclAPI".url=https://cve.circl.lu quarkus.rest-client."org.eclipsefoundation.cve.api.CveCirclAPI".url=https://cve.circl.lu
quarkus.rest-client."org.eclipsefoundation.cve.api.GithubCveAPI".url=https://raw.githubusercontent.com/CVEProject/cvelist/master
eclipse.cve.provider=stubbed #eclipse.cve.provider=stubbed
quarkus.oidc.enabled=false quarkus.oidc.enabled=false
quarkus.keycloak.devservices.enabled=false quarkus.keycloak.devservices.enabled=false
\ No newline at end of file
package org.eclipsefoundation.cve; package org.eclipsefoundation.cve.resources;
import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.given;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath; import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import org.eclipsefoundation.cve.model.CveCSVData;
import org.eclipsefoundation.cve.model.mapper.CveDataMapper;
import org.eclipsefoundation.cve.service.GoogleAPIService;
import org.eclipsefoundation.cve.service.impl.StubbedCveSourceService;
import org.eclipsefoundation.cve.test.helpers.SchemaNamespaceHelper; import org.eclipsefoundation.cve.test.helpers.SchemaNamespaceHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.mapstruct.factory.Mappers;
import org.mockito.Mockito;
import com.google.api.services.drive.Drive;
import com.google.inject.Inject;
import io.quarkus.test.junit.QuarkusMock;
import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@QuarkusTest @QuarkusTest
@TestInstance(Lifecycle.PER_CLASS)
class CveResourceTest { class CveResourceTest {
public final static String CVES_BASE_URL = "/cve"; public final static String CVES_BASE_URL = "/cve";
public final static String CVE_BASE_URL = CVES_BASE_URL + "/{id}"; public final static String CVE_BASE_URL = CVES_BASE_URL + "/{id}";
public final static String VALID_CVE_ID = "cve-2020-27225"; public final static String VALID_CVE_ID = "cve-2020-27225";
public final static String INVALID_CVE_ID = "jim-bob-2022"; public final static String INVALID_CVE_ID = "jim-bob-2022";
@Test @Test
void getAll_success() { void getAll_success() {
given() given().when().get(CVES_BASE_URL).then().statusCode(200);
.when()
.get(CVES_BASE_URL)
.then()
.statusCode(200);
} }
@Test @Test
void getAll_success_validResponseFormat() { void getAll_success_validResponseFormat() {
given() given().when().get(CVES_BASE_URL).then().contentType(ContentType.JSON).statusCode(200);
.when()
.get(CVES_BASE_URL)
.then()
.contentType(ContentType.JSON)
.statusCode(200);
} }
@Test @Test
void getAll_success_matchingSpec() { void getAll_success_matchingSpec() {
given() given().when().get(CVES_BASE_URL).then().assertThat().body(matchesJsonSchemaInClasspath(SchemaNamespaceHelper.CVES_SCHEMA_PATH));
.when()
.get(CVES_BASE_URL)
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath(SchemaNamespaceHelper.CVES_SCHEMA_PATH));
} }
@Test @Test
void getAll_failure_invalidFormatTEXT() { void getAll_failure_invalidFormatTEXT() {
given() given().accept(ContentType.TEXT).when().get(CVES_BASE_URL).then().statusCode(500);
.accept(ContentType.TEXT)
.when()
.get(CVES_BASE_URL)
.then()
.statusCode(500);
} }
@Test @Test
void getAll_failure_invalidFormatXML() { void getAll_failure_invalidFormatXML() {
given() given().accept(ContentType.XML).when().get(CVES_BASE_URL).then().statusCode(500);
.accept(ContentType.XML)
.when()
.get(CVES_BASE_URL)
.then()
.statusCode(500);
} }
@Test @Test
void getById_success() { void getById_success() {
given() given().when().get(CVE_BASE_URL, VALID_CVE_ID).then().statusCode(200);
.when()
.get(CVE_BASE_URL, VALID_CVE_ID)
.then()
.statusCode(200);
} }
@Test @Test
void getById_success_validResponseFormat() { void getById_success_validResponseFormat() {
given() given().when().get(CVE_BASE_URL, VALID_CVE_ID).then().contentType(ContentType.JSON).statusCode(200);
.when()
.get(CVE_BASE_URL, VALID_CVE_ID)
.then()
.contentType(ContentType.JSON)
.statusCode(200);
} }
@Test @Test
void getById_success_matchingSpec() { void getById_success_matchingSpec() {
given() given()
.when() .when()
.get(CVE_BASE_URL, VALID_CVE_ID) .get(CVE_BASE_URL, VALID_CVE_ID)
.then() .then()
.assertThat() .assertThat()
.body(matchesJsonSchemaInClasspath(SchemaNamespaceHelper.CVE_SCHEMA_PATH)); .body(matchesJsonSchemaInClasspath(SchemaNamespaceHelper.CVE_SCHEMA_PATH));
} }
@Test @Test
void getById_failure_invalidId() { void getById_failure_invalidId() {
given() given().when().get(CVE_BASE_URL, INVALID_CVE_ID).then().statusCode(404);
.when()
.get(CVE_BASE_URL, INVALID_CVE_ID)
.then()
.statusCode(404);
} }
@Test @Test
void getById_failure_invalidFormatTEXT() { void getById_failure_invalidFormatTEXT() {
given() given().accept(ContentType.TEXT).when().get(CVE_BASE_URL, VALID_CVE_ID).then().statusCode(500);
.accept(ContentType.TEXT)
.when()
.get(CVE_BASE_URL, VALID_CVE_ID)
.then()
.statusCode(500);
} }
@Test @Test
void getById_failure_invalidFormatXML() { void getById_failure_invalidFormatXML() {
given() given().accept(ContentType.XML).when().get(CVE_BASE_URL, VALID_CVE_ID).then().statusCode(500);
.accept(ContentType.XML)
.when()
.get(CVE_BASE_URL, VALID_CVE_ID)
.then()
.statusCode(500);
} }
} }
\ No newline at end of file
...@@ -41,7 +41,7 @@ class StubbedCveSourceServiceTest { ...@@ -41,7 +41,7 @@ class StubbedCveSourceServiceTest {
.assertTrue(cveService .assertTrue(cveService
.getAllCves() .getAllCves()
.stream() .stream()
.anyMatch(cve -> !cve.getStatus().equalsIgnoreCase("completed"))); .anyMatch(cve -> !cve.getStatus().equalsIgnoreCase("complete")));
} }
@Test @Test
...@@ -51,13 +51,13 @@ class StubbedCveSourceServiceTest { ...@@ -51,13 +51,13 @@ class StubbedCveSourceServiceTest {
} }
@Test @Test
void getPublicCves_success_containsNonCompletedCve() { void getPublicCves_success_containsNoncompleteCve() {
// assert that the stubbed service will return at least one hidden cve for all // assert that the stubbed service will return at least one hidden cve for all
Assertions Assertions
.assertTrue(cveService .assertTrue(cveService
.getPublicCves() .getPublicCves()
.stream() .stream()
.noneMatch(cve -> !cve.getStatus().equalsIgnoreCase("completed"))); .noneMatch(cve -> !cve.getStatus().equalsIgnoreCase("complete")));
} }
@Test @Test
...@@ -74,16 +74,16 @@ class StubbedCveSourceServiceTest { ...@@ -74,16 +74,16 @@ class StubbedCveSourceServiceTest {
} }
@Test @Test
void getCvesForProject_success_filtersNonCompleted() { void getCvesForProject_success_filtersNoncomplete() {
List<CveData> cves = cveService.getForProject(PROJECT_NAME_PRESENT, false); List<CveData> cves = cveService.getForProject(PROJECT_NAME_PRESENT, false);
Assertions.assertTrue(cves != null && !cves.isEmpty()); Assertions.assertTrue(cves != null && !cves.isEmpty());
Assertions.assertTrue(cves.stream().allMatch(cve -> cve.getStatus().equalsIgnoreCase("completed"))); Assertions.assertTrue(cves.stream().allMatch(cve -> cve.getStatus().equalsIgnoreCase("complete")));
} }
@Test @Test
void getCvesForProject_success_allowsNonCompleted() { void getCvesForProject_success_allowsNoncomplete() {
List<CveData> cves = cveService.getForProject(PROJECT_NAME_PRESENT, true); List<CveData> cves = cveService.getForProject(PROJECT_NAME_PRESENT, true);
Assertions.assertTrue(cves != null && !cves.isEmpty()); Assertions.assertTrue(cves != null && !cves.isEmpty());
Assertions.assertTrue(cves.stream().anyMatch(cve -> !cve.getStatus().equalsIgnoreCase("completed"))); Assertions.assertTrue(cves.stream().anyMatch(cve -> !cve.getStatus().equalsIgnoreCase("complete")));
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment