-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge: Main <- juwon/#8-Controller-Service
[Feat] Controller, Service Layer 구현
- Loading branch information
Showing
15 changed files
with
302 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
.../pipeline/manager/ManagerApplication.java → ...com/analyzer/sbom/ManagerApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
manager/src/main/java/com/analyzer/sbom/common/CommonResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.analyzer.sbom.common; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@JsonInclude(JsonInclude.Include.NON_NULL) | ||
public class CommonResponse<T> { | ||
|
||
private String responseCode; | ||
private String responseMessage; | ||
private T data; | ||
|
||
@Builder | ||
private CommonResponse(String responseCode, String responseMessage, T data) { | ||
this.responseCode = responseCode; | ||
this.responseMessage = responseMessage; | ||
this.data = data; | ||
} | ||
|
||
public static CommonResponse resWithoutData (final String responseCode, final String responseMessage) { | ||
return CommonResponse.builder() | ||
.responseCode(responseCode) | ||
.responseMessage(responseMessage) | ||
.build(); | ||
} | ||
public static <T> CommonResponse<T> resWithData (final String responseCode, final String responseMessage, final T data) { | ||
return CommonResponse.<T>builder() | ||
.responseCode(responseCode) | ||
.responseMessage(responseMessage) | ||
.data(data) | ||
.build(); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
manager/src/main/java/com/analyzer/sbom/config/SwaggerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.analyzer.sbom.config; | ||
|
||
import io.swagger.v3.oas.models.Components; | ||
import io.swagger.v3.oas.models.OpenAPI; | ||
import io.swagger.v3.oas.models.info.Info; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class SwaggerConfig { | ||
@Bean | ||
public OpenAPI openAPI() { | ||
return new OpenAPI() | ||
.components(new Components()) | ||
.info(apiInfo()); | ||
} | ||
|
||
private Info apiInfo() { | ||
return new Info() | ||
.title("SBOM Vulnerability Analyzer") | ||
.description("SBOM에서 확인 가능한 취약점에 대한 정보를 정리해주는 애플리케이션 명세입니다") | ||
.version("1.0.0"); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
manager/src/main/java/com/analyzer/sbom/config/WebClientConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.analyzer.sbom.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
|
||
@Configuration | ||
public class WebClientConfig { | ||
@Bean | ||
public WebClient.Builder webclientBuilder() { | ||
return WebClient.builder() | ||
.defaultHeader(HttpHeaders.ACCEPT, "*/*") | ||
.defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json"); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
manager/src/main/java/com/analyzer/sbom/controller/SbomController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.analyzer.sbom.controller; | ||
|
||
import com.analyzer.sbom.common.CommonResponse; | ||
import com.analyzer.sbom.dto.response.SbomResponseDto; | ||
import com.analyzer.sbom.service.SbomService; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
import static org.springframework.http.HttpStatus.*; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/sbom") | ||
@Tag(name = "SBOM", description = "SBOM controller") | ||
public class SbomController { | ||
|
||
private final SbomService sbomService; | ||
|
||
@GetMapping | ||
@Operation(summary = "SBOM Scan", description = "Scan Software Bill of Materials") | ||
public ResponseEntity<CommonResponse<JsonNode>> scanSBOM(@RequestParam String token, @RequestParam String projectId, @RequestParam String baseUrl) throws JsonProcessingException { | ||
JsonNode sbomResult = sbomService.scanVulnerability(token, projectId, baseUrl); | ||
return ResponseEntity.status(OK).body(CommonResponse.resWithData("SBOM_SCAN_COMPLETED", "SBOM 스캔이 완료되었습니다", sbomResult)); | ||
} | ||
|
||
@GetMapping("/report") | ||
@Operation(summary = "Get SBOM Report", description = "Get Software Bill of Materials' security report") | ||
public ResponseEntity<CommonResponse<List<SbomResponseDto>>> getReport(@RequestParam String token, @RequestParam String projectId, @RequestParam String baseUrl) throws IOException { | ||
List<SbomResponseDto> sbomReport = sbomService.generateReport(token, projectId, baseUrl); | ||
return ResponseEntity.status(OK).body(CommonResponse.resWithData("SBOM_REPORT_GENERATED", "SBOM 보안 보고서가 생성되었습니다", sbomReport)); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
manager/src/main/java/com/analyzer/sbom/dto/response/SbomResponseDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.analyzer.sbom.dto.response; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
import java.util.List; | ||
|
||
@Getter | ||
public class SbomResponseDto { | ||
private String name; | ||
private String purl; | ||
private String version; | ||
private String group; | ||
private String vulnId; | ||
private String severity; | ||
private String description; | ||
private String source; | ||
private String referenceUrl; | ||
private String suggestion; | ||
private List<String> suggestionUrl; | ||
|
||
@Builder | ||
public SbomResponseDto(String name, String purl, String version, String group, String vulnId, String severity, String description, String source, String referenceUrl, String suggestion, List<String> suggestionUrl) { | ||
this.name = name; | ||
this.purl = purl; | ||
this.version = version; | ||
this.group = group; | ||
this.vulnId = vulnId; | ||
this.severity = severity; | ||
this.description = description; | ||
this.source = source; | ||
this.referenceUrl = referenceUrl; | ||
this.suggestion = suggestion; | ||
this.suggestionUrl = suggestionUrl; | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...ger/exception/GlobalExceptionHandler.java → ...bom/exception/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
manager/src/main/java/com/analyzer/sbom/service/SbomService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package com.analyzer.sbom.service; | ||
|
||
import com.analyzer.sbom.dto.response.SbomResponseDto; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.RequiredArgsConstructor; | ||
import org.jsoup.Jsoup; | ||
import org.jsoup.nodes.Document; | ||
import org.jsoup.nodes.Element; | ||
import org.jsoup.select.Elements; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class SbomService { | ||
|
||
private final WebClient.Builder webClientBuilder; | ||
private final ObjectMapper objectMapper; | ||
|
||
@Value("${webclient.nvd}") | ||
private String cveUrl; | ||
|
||
@Value("${webclient.nvdSelector}") | ||
private String nvdSelector; | ||
|
||
@Value("${webclient.snyk}") | ||
private String snykUrl; | ||
|
||
@Value("${webclient.snykXpath}") | ||
private String snykXpath; | ||
|
||
@Value("${webclient.snykSearchXpath}") | ||
private String snykSearchXpath; | ||
|
||
@Value("${webclient.snykBase}") | ||
private String snykBase; | ||
|
||
public JsonNode scanVulnerability(String token, String projectId, String baseUrl) throws JsonProcessingException { | ||
String jsonData = getAPI(token, projectId, baseUrl); | ||
return objectMapper.readTree(jsonData); | ||
} | ||
|
||
public List<SbomResponseDto> generateReport(String token, String projectId, String baseUrl) throws IOException { | ||
String sbomResult = getAPI(token, projectId, baseUrl); | ||
|
||
List<SbomResponseDto> sbomReport = new ArrayList<>(); | ||
|
||
JsonNode jsonNode = objectMapper.readTree(sbomResult).get("findings"); | ||
if (jsonNode.isArray()) { | ||
for (JsonNode finding : jsonNode) { | ||
JsonNode component = finding.get("component"); | ||
String name = isExist(component, "name") ? component.get("name").asText() : ""; | ||
String version = isExist(component, "version") ? component.get("version").asText() : ""; | ||
String purl = isExist(component, "purl") ? component.get("purl").asText() : ""; | ||
String group = isExist(component, "group") ? component.get("group").asText() : ""; | ||
|
||
JsonNode attribution = finding.get("attribution"); | ||
String suggestionLink = isExist(attribution, "referenceUrl") ? attribution.get("referenceUrl").asText() : ""; | ||
|
||
JsonNode vulnerability = finding.get("vulnerability"); | ||
String severity = isExist(vulnerability, "severity") ? vulnerability.get("severity").asText() : ""; | ||
String vulnId = isExist(vulnerability, "vulnId") ? vulnerability.get("vulnId").asText() : ""; | ||
String source = isExist(vulnerability, "source") ? vulnerability.get("source").asText() : ""; | ||
String description = isExist(vulnerability, "description") ? vulnerability.get("description").asText() : ""; | ||
|
||
String referenceUrl = cveUrl + vulnId; | ||
|
||
List<String> suggestionUrl = getSuggestionUrl(referenceUrl); | ||
suggestionUrl.add(getSuggestion(snykUrl + vulnId, true)); | ||
if(!Objects.equals(suggestionLink, "")) suggestionUrl.add(suggestionLink); | ||
|
||
String suggestion = getSuggestion(snykUrl + vulnId, false); | ||
|
||
SbomResponseDto sbomResponseDto = SbomResponseDto.builder() | ||
.name(name) | ||
.version(version) | ||
.purl(purl) | ||
.group(group) | ||
.severity(severity) | ||
.vulnId(vulnId) | ||
.source(source) | ||
.description(description) | ||
.referenceUrl(referenceUrl) | ||
.suggestionUrl(suggestionUrl) | ||
.suggestion(suggestion) | ||
.build(); | ||
|
||
sbomReport.add(sbomResponseDto); | ||
} | ||
} | ||
return sbomReport; | ||
} | ||
|
||
private String getAPI(String token, String projectId, String baseUrl) { | ||
WebClient webClient = webClientBuilder | ||
.baseUrl(baseUrl) | ||
.defaultHeader("X-Api-Key", token) | ||
.build(); | ||
|
||
return webClient | ||
.get() | ||
.uri("/api/v1/finding/project/{projectId}/export", projectId) | ||
.retrieve() | ||
.bodyToMono(String.class) | ||
.block(); | ||
} | ||
|
||
private boolean isExist(JsonNode source, String fieldName) { | ||
return source.has(fieldName); | ||
} | ||
|
||
private List<String> getSuggestionUrl(String cveUrl) throws IOException { | ||
Document doc = Jsoup.connect(cveUrl).get(); | ||
Element table = doc.select(nvdSelector).first(); | ||
Elements tdElements = Objects.requireNonNull(table).select("td"); | ||
|
||
return tdElements.stream() | ||
.flatMap(td -> td.select("a").stream()) | ||
.map(Element::text) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private String getSuggestion(String snykUrl, Boolean isLink) throws IOException { | ||
Document doc = Jsoup.connect(snykUrl).get(); | ||
String link = String.join("", doc.selectXpath(snykXpath).eachAttr("href")); | ||
String suggestionLink = snykBase + link; | ||
|
||
Document solutionDoc = Jsoup.connect(suggestionLink).get(); | ||
String suggestion = solutionDoc.selectXpath(snykSearchXpath).text(); | ||
|
||
return isLink ? suggestionLink : suggestion; | ||
} | ||
|
||
} |
5 changes: 0 additions & 5 deletions
5
manager/src/main/java/com/pipeline/manager/common/CommonResponse.java
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
manager/src/main/java/com/pipeline/manager/config/SwaggerConfig.java
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
manager/src/main/java/com/pipeline/manager/config/WebClientConfig.java
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
manager/src/main/java/com/pipeline/manager/controller/SbomController.java
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
manager/src/main/java/com/pipeline/manager/service/SbomService.java
This file was deleted.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
...line/manager/ManagerApplicationTests.java → ...nalyzer/sbom/ManagerApplicationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters