Skip to content

Commit

Permalink
Use views instead of native queries where possible (#33)
Browse files Browse the repository at this point in the history
This PR changes a lot about how glvd-api works.

The biggest change is that we now rely on VIEWs in the db instead of having native queries in our repositories where possible. This simplifies the code a lot and should also make it easier to implement sorting gardenlinux/glvd#95 and filtering gardenlinux/glvd#94 in the api.
  • Loading branch information
fwilhe authored Sep 11, 2024
1 parent 39afa61 commit 76c1264
Show file tree
Hide file tree
Showing 28 changed files with 255 additions and 587 deletions.
11 changes: 0 additions & 11 deletions api-examples/Get CVE by Id.bru

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
meta {
name: Get CVEs by Distro Codename Packages
name: Get CVEs by Gardenlinux Version Packages
type: http
seq: 4
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
meta {
name: Get CVEs by Distro Codename
name: Get CVEs by Gardenlinux Version
type: http
seq: 3
}
Expand Down
24 changes: 0 additions & 24 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,6 @@ CAUTION: This document and the API are work in progress and subject to change at

CAUTION: This document uses `glvd.gardenlinux.io` as the hostname in samples. This does not work currently. When running locally, replace it with `localhost:8080`, or with the real hostname of your demo system.

=== Check running app

For verifying that the app is running and has a connection to the database, you may query the `readiness` endpoint:

include::{snippets}/readiness/curl-request.adoc[]

The expected response looks like this:

include::{snippets}/readiness/http-response.adoc[]

If this worked, your GLVD api is deployed and it has access to the configured database management system.

=== Get a CVE by id

To query a single CVE by its id, you maye use the `cves` endpoint:

include::{snippets}/getCve/curl-request.adoc[]

The expected response looks like this:

include::{snippets}/getCve/http-response.adoc[]

The `data` field contains a full copy of the CVE as provided by https://nvd.nist.gov[NIST NVD].

=== Get a list of CVEs by distro

To query all CVEs for a given distribution by version, you may use this endpoint:
Expand Down
24 changes: 0 additions & 24 deletions src/main/java/io/gardenlinux/glvd/AppController.java

This file was deleted.

44 changes: 29 additions & 15 deletions src/main/java/io/gardenlinux/glvd/GlvdController.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.db.CveEntity;
import io.gardenlinux.glvd.db.SourcePackage;
import io.gardenlinux.glvd.db.SourcePackageCve;
import io.gardenlinux.glvd.exceptions.NotFoundException;
import jakarta.annotation.Nonnull;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,7 +13,7 @@
import java.util.List;

@RestController
@RequestMapping(value = "/v1/cves", produces = MediaType.APPLICATION_JSON_VALUE)
@RequestMapping(value = "/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class GlvdController {

@Nonnull
Expand All @@ -24,22 +23,37 @@ public GlvdController(@Nonnull GlvdService glvdService) {
this.glvdService = glvdService;
}

@GetMapping("/{cveId}")
ResponseEntity<CveEntity> getCveId(@PathVariable("cveId") final String cveId) throws NotFoundException {
return ResponseEntity.ok().body(glvdService.getCve(cveId));
@GetMapping("/cves/{distro}/{gardenlinuxVersion}")
ResponseEntity<List<SourcePackageCve>> getCveDistro(
@PathVariable final String gardenlinuxVersion) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(gardenlinuxVersion));
}

@GetMapping("/{distro}/{distroVersion}")
ResponseEntity<List<SourcePackageCve>> getCveDistro(@PathVariable final String distro,
@PathVariable final String distroVersion) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(distro, distroVersion));
@GetMapping("/cves/{distro}/{gardenlinuxVersion}/packages/{packageList}")
ResponseEntity<List<SourcePackageCve>> getCvePackages(
@PathVariable final String gardenlinuxVersion, @PathVariable final String packageList) {
var cveForPackages = glvdService.getCveForPackages(gardenlinuxVersion, packageList);
return ResponseEntity.ok().body(cveForPackages);
}

@GetMapping("/{distro}/{distroVersion}/packages/{packageList}")
ResponseEntity<List<SourcePackageCve>> getCvePackages(@PathVariable final String distro,
@PathVariable final String distroVersion, @PathVariable final String packageList) {
var cveForPackages = glvdService.getCveForPackages(distro, distroVersion, packageList);
return ResponseEntity.ok().body(cveForPackages);
@GetMapping("/packages/distro/{distro}/{gardenlinuxVersion}")
ResponseEntity<List<SourcePackage>> packagesForDistro(@PathVariable final String gardenlinuxVersion) {
return ResponseEntity.ok(glvdService.getPackagesForDistro(gardenlinuxVersion));
}

@GetMapping("/packages/{sourcePackage}")
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilities(@PathVariable final String sourcePackage) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilities(sourcePackage));
}

@GetMapping("/packages/{sourcePackage}/{sourcePackageVersion}")
ResponseEntity<List<SourcePackageCve>> packageWithVulnerabilitiesByVersion(@PathVariable final String sourcePackage, @PathVariable final String sourcePackageVersion) {
return ResponseEntity.ok(glvdService.getPackageWithVulnerabilitiesByVersion(sourcePackage, sourcePackageVersion));
}

@GetMapping("/packages/distro/{distro}/{gardenlinuxVersion}/{cveId}")
ResponseEntity<List<SourcePackageCve>> packagesByVulnerability(@PathVariable final String gardenlinuxVersion, @PathVariable final String cveId) {
return ResponseEntity.ok(glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId));
}

}
73 changes: 17 additions & 56 deletions src/main/java/io/gardenlinux/glvd/GlvdService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.db.*;
import io.gardenlinux.glvd.dto.Readiness;
import io.gardenlinux.glvd.exceptions.DbNotConnectedException;
import io.gardenlinux.glvd.exceptions.NotFoundException;
import jakarta.annotation.Nonnull;
import org.springframework.stereotype.Service;

Expand All @@ -13,73 +10,37 @@
public class GlvdService {

@Nonnull
private final CveRepository cveRepository;
private final SourcePackageCveRepository sourcePackageCveRepository;

@Nonnull
private final PackagesRepository packagesRepository;
private final SourcePackageRepository sourcePackageRepository;

@Nonnull
private final HealthCheckRepository healthCheckRepository;

public GlvdService(@Nonnull CveRepository cveRepository, @Nonnull PackagesRepository packagesRepository, @Nonnull HealthCheckRepository healthCheckRepository) {
this.cveRepository = cveRepository;
this.packagesRepository = packagesRepository;
this.healthCheckRepository = healthCheckRepository;
}

public Readiness getReadiness() throws DbNotConnectedException {
try {
var connection = healthCheckRepository.checkDbConnection();
return new Readiness(connection);
} catch (Exception e) {
throw new DbNotConnectedException(e);
}
}

// Not the most elegant solution. This might be replaced by a VIEW in the database,
// or some other feature in spring data jpa?
private SourcePackageCve parseDbResponse(String input) {
var parts = input.split(",");
var packageName = parts[0];
var cveId = parts[1];
var cvePublishedDate = parts[2];
return new SourcePackageCve(cveId, cvePublishedDate, packageName);
}

public CveEntity getCve(String cveId) throws NotFoundException {
return cveRepository.findById(cveId).orElseThrow(NotFoundException::new);
}

public List<SourcePackageCve> getCveForDistribution(String distro, String distroVersion) {
return cveRepository.cvesForDistribution(distro, distroVersion).stream().map(this::parseDbResponse).toList();
public GlvdService(@Nonnull SourcePackageCveRepository sourcePackageCveRepository, @Nonnull SourcePackageRepository sourcePackageRepository) {
this.sourcePackageCveRepository = sourcePackageCveRepository;
this.sourcePackageRepository = sourcePackageRepository;
}

public List<SourcePackageCve> getCveForPackages(String distro, String distroVersion, String packages) {
return cveRepository.cvesForPackageList(distro, distroVersion,"{"+packages+"}").stream().map(this::parseDbResponse).toList();
public List<SourcePackageCve> getCveForDistribution(String gardenlinuxVersion) {
return sourcePackageCveRepository.findByGardenlinuxVersion(gardenlinuxVersion);
}

public List<String> getPackagesForDistro(String distro, String distroVersion) {
return cveRepository.packagesForDistribution(distro, distroVersion);
public List<SourcePackageCve> getCveForPackages(String gardenlinuxVersion, String packages) {
return sourcePackageCveRepository.findBySourcePackageNameInAndGardenlinuxVersion("{"+packages+"}", gardenlinuxVersion);
}

private PackageEntity parseDbResponsePackageWithVulnerabilities(String input) {
var parts = input.split(",");
var cveId = parts[0];
var debSource = parts[1];
var debVersion = parts[2];
var debsecVulnerable = parts[3];
return new PackageEntity(cveId, debSource, debVersion, debsecVulnerable);
public List<SourcePackage> getPackagesForDistro(String gardenlinuxVersion) {
return sourcePackageRepository.findByGardenlinuxVersion(gardenlinuxVersion);
}

public List<PackageEntity> getPackageWithVulnerabilities(String sourcePackage) {
return packagesRepository.packageWithVulnerabilities(sourcePackage);
public List<SourcePackageCve> getPackageWithVulnerabilities(String sourcePackage) {
return sourcePackageCveRepository.findBySourcePackageName(sourcePackage);
}

public List<PackageEntity> getPackageWithVulnerabilitiesByVersion(String sourcePackage, String sourcePackageVersion) {
return packagesRepository.packageWithVulnerabilitiesByVersion(sourcePackage, sourcePackageVersion);
public List<SourcePackageCve> getPackageWithVulnerabilitiesByVersion(String sourcePackage, String sourcePackageVersion) {
return sourcePackageCveRepository.findBySourcePackageNameAndSourcePackageVersion(sourcePackage, sourcePackageVersion);
}

public List<PackageEntity> getPackagesByVulnerability(String distro, String distroVersion, String cveId) {
return packagesRepository.packagesByVulnerability(distro, distroVersion, cveId);
public List<SourcePackageCve> getPackagesByVulnerability(String gardenlinuxVersion, String cveId) {
return sourcePackageCveRepository.findByCveIdAndGardenlinuxVersion(cveId, gardenlinuxVersion);
}
}
44 changes: 0 additions & 44 deletions src/main/java/io/gardenlinux/glvd/PackageController.java

This file was deleted.

36 changes: 15 additions & 21 deletions src/main/java/io/gardenlinux/glvd/UiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,49 @@ public UiController(@Nonnull GlvdService glvdService) {

@GetMapping("/getPackagesForDistro")
public String getPackagesForDistro(
@RequestParam(name = "distro", required = false, defaultValue = "gardenlinux") String distro,
@RequestParam(name = "version", required = true) String version,
@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
Model model) {
var packages = glvdService.getPackagesForDistro(distro, version);
var packages = glvdService.getPackagesForDistro(gardenlinuxVersion);
model.addAttribute("packages", packages);
model.addAttribute("distro", distro);
model.addAttribute("version", version);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
return "getPackagesForDistro";
}

@GetMapping("/getCveForDistribution")
public String getCveForDistribution(
@RequestParam(name = "distro", required = false, defaultValue = "gardenlinux") String distro,
@RequestParam(name = "version", required = true) String version,
@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
Model model
) {
var sourcePackageCves = glvdService.getCveForDistribution(distro, version);
var sourcePackageCves = glvdService.getCveForDistribution(gardenlinuxVersion);
model.addAttribute("sourcePackageCves", sourcePackageCves);
model.addAttribute("distro", distro);
model.addAttribute("version", version);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
return "getCveForDistribution";
}

@GetMapping("/getCveForPackages")
public String getCveForPackages(
@RequestParam(name = "distro", required = false, defaultValue = "gardenlinux") String distro,
@RequestParam(name = "version", required = true) String version,

@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(name = "packages", required = true) String packages,
Model model
) {
var sourcePackageCves = glvdService.getCveForPackages(distro, version, packages);
var sourcePackageCves = glvdService.getCveForPackages(gardenlinuxVersion, packages);
model.addAttribute("sourcePackageCves", sourcePackageCves);
model.addAttribute("distro", distro);
model.addAttribute("version", version);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
model.addAttribute("packages", packages);
return "getCveForPackages";
}

@GetMapping("/getPackagesByVulnerability")
public String getPackagesByVulnerability(
@RequestParam(name = "distro", required = false, defaultValue = "gardenlinux") String distro,
@RequestParam(name = "version", required = true) String version,

@RequestParam(name = "gardenlinuxVersion", required = true) String gardenlinuxVersion,
@RequestParam(name = "cveId", required = true) String cveId,
Model model
) {
var packageEntities = glvdService.getPackagesByVulnerability(distro, version, cveId);
model.addAttribute("packageEntities", packageEntities);
model.addAttribute("distro", distro);
model.addAttribute("version", version);
var sourcePackageCves = glvdService.getPackagesByVulnerability(gardenlinuxVersion, cveId);
model.addAttribute("sourcePackageCves", sourcePackageCves);
model.addAttribute("gardenlinuxVersion", gardenlinuxVersion);
model.addAttribute("cveId", cveId);
return "getPackagesByVulnerability";
}
Expand Down
Loading

0 comments on commit 76c1264

Please sign in to comment.