Skip to content

Commit 84b9677

Browse files
majaberejberejmaj
andauthored
Feature/new run scan endpoint (#79)
* new run-orch endpoint * removed printing coderepo details in error message --------- Co-authored-by: berejmaj <maja.berej@orange.com>
1 parent e54c452 commit 84b9677

7 files changed

Lines changed: 202 additions & 8 deletions

File tree

backend/src/main/java/io/mixeway/mixewayflowapi/api/coderepo/controller/CodeRepoController.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import io.mixeway.mixewayflowapi.api.coderepo.dto.*;
44
import io.mixeway.mixewayflowapi.api.coderepo.service.CodeRepoApiService;
5+
import io.mixeway.mixewayflowapi.api.gitlabcicd.dto.GitLabCICDRequestDto;
56
import io.mixeway.mixewayflowapi.db.entity.CodeRepo;
7+
import io.mixeway.mixewayflowapi.db.entity.CodeRepoBranch;
68
import io.mixeway.mixewayflowapi.domain.coderepo.CreateCodeRepoService;
79
import io.mixeway.mixewayflowapi.domain.coderepo.DeleteCodeRepoService;
810
import io.mixeway.mixewayflowapi.exceptions.CodeRepoNotFoundException;
@@ -193,4 +195,31 @@ public ResponseEntity<StatusDTO> deleteCodeRepo(@PathVariable("id") Long id, Pri
193195
return new ResponseEntity<>(new StatusDTO("Internal server error"), HttpStatus.INTERNAL_SERVER_ERROR);
194196
}
195197
}
198+
199+
@PreAuthorize("hasAuthority('USER')")
200+
@PostMapping(value = "/api/v1/coderepo/run-orch")
201+
public ResponseEntity<String> runRepoScan(@RequestHeader("X-API-KEY") String apiKey, @Valid @RequestBody RunOrchRequestDto requestDto) {
202+
try {
203+
String repoUrl = requestDto.getRepoUrl();
204+
Long teamId = requestDto.getTeamId();
205+
String branch = requestDto.getBranch();
206+
String domain = requestDto.getDomain();
207+
208+
if (!codeRepoApiService.isRepoInTeamById(repoUrl, teamId)) {
209+
String errorMessage = String.format("The repository with URL '%s' does not belong to the team with ID '%d'.", repoUrl, teamId);
210+
return new ResponseEntity<>(errorMessage, HttpStatus.FORBIDDEN);
211+
}
212+
213+
if (!codeRepoApiService.isValidApiKey(apiKey, repoUrl)) {
214+
String errorMessage = String.format("The provided API key is invalid for the repository with URL '%s'.", repoUrl);
215+
return new ResponseEntity<>(errorMessage, HttpStatus.UNAUTHORIZED);
216+
}
217+
218+
String scanDetails = codeRepoApiService.runScan(repoUrl, branch, domain);
219+
220+
return new ResponseEntity<>(scanDetails, HttpStatus.OK);
221+
} catch (Exception e){
222+
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
223+
}
224+
}
196225
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.mixeway.mixewayflowapi.api.coderepo.dto;
2+
import jakarta.validation.constraints.NotNull;
3+
import lombok.Data;
4+
5+
@Data
6+
public class RunOrchRequestDto {
7+
private String repoUrl;
8+
private Long teamId;
9+
private String branch;
10+
private String domain;
11+
12+
public RunOrchRequestDto() {}
13+
14+
@NotNull(message = "RepoURL must not be null.")
15+
public String getRepoUrl() {
16+
return repoUrl;
17+
}
18+
19+
@NotNull(message = "TeamID must not be null.")
20+
public Long getTeamId() {
21+
return teamId;
22+
}
23+
24+
public String getBranch() { return branch; }
25+
26+
public String getDomain() {
27+
return domain;
28+
}
29+
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.mixeway.mixewayflowapi.api.coderepo.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class RunOrchScanDetailsDto {
13+
String name;
14+
String description;
15+
String explanation;
16+
String recommendation;
17+
String location;
18+
String source;
19+
String status;
20+
String severity;
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.mixeway.mixewayflowapi.api.coderepo.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Data
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class RunOrchScanReportDto {
15+
16+
String repoUrl;
17+
String branch;
18+
String linkToScanDetails;
19+
20+
List<RunOrchScanDetailsDto> findings;
21+
}

backend/src/main/java/io/mixeway/mixewayflowapi/api/coderepo/service/CodeRepoApiService.java

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
package io.mixeway.mixewayflowapi.api.coderepo.service;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
35
import io.mixeway.mixewayflowapi.api.coderepo.dto.GetCodeReposResponseDto;
4-
import io.mixeway.mixewayflowapi.db.entity.CodeRepo;
5-
import io.mixeway.mixewayflowapi.db.entity.Team;
6+
import io.mixeway.mixewayflowapi.api.coderepo.dto.RunOrchScanDetailsDto;
7+
import io.mixeway.mixewayflowapi.api.coderepo.dto.RunOrchScanReportDto;
8+
import io.mixeway.mixewayflowapi.api.gitlabcicd.dto.GitLabCICDDetailsResponseDto;
9+
import io.mixeway.mixewayflowapi.db.entity.*;
10+
import io.mixeway.mixewayflowapi.db.repository.UserRepository;
611
import io.mixeway.mixewayflowapi.domain.coderepo.FindCodeRepoService;
712
import io.mixeway.mixewayflowapi.domain.coderepo.UpdateCodeRepoService;
13+
import io.mixeway.mixewayflowapi.domain.finding.FindFindingService;
814
import io.mixeway.mixewayflowapi.domain.team.FindTeamService;
915
import io.mixeway.mixewayflowapi.exceptions.CodeRepoNotFoundException;
1016
import io.mixeway.mixewayflowapi.exceptions.TeamNotFoundException;
11-
import io.mixeway.mixewayflowapi.exceptions.UnauthorizedException;
1217
import io.mixeway.mixewayflowapi.scanmanager.service.ScanManagerService;
1318
import io.mixeway.mixewayflowapi.utils.PermissionFactory;
1419
import lombok.RequiredArgsConstructor;
1520
import lombok.extern.log4j.Log4j2;
21+
import org.aspectj.apache.bcel.classfile.Code;
1622
import org.springframework.stereotype.Service;
1723

1824
import java.security.Principal;
19-
import java.util.List;
25+
import java.util.*;
2026
import java.util.stream.Collectors;
2127

2228
@Service
@@ -28,6 +34,8 @@ public class CodeRepoApiService {
2834
private final PermissionFactory permissionFactory;
2935
private final FindTeamService findTeamService;
3036
private final UpdateCodeRepoService updateCodeRepoService;
37+
private final UserRepository userRepository;
38+
private final FindFindingService findFindingService;
3139

3240
public List<GetCodeReposResponseDto> getRepos(Principal principal) {
3341
return findCodeRepoService.getCodeReposResponseDtos(principal);
@@ -101,4 +109,89 @@ public void renameCodeRepo(Long repoId, String newName, Principal principal) {
101109
permissionFactory.canUserManageTeam(repo.getTeam(), principal);
102110
updateCodeRepoService.renameCodeRepo(repo, newName);
103111
}
112+
113+
public Boolean isRepoInTeamById(String repoUrl, Long teamId) {
114+
try {
115+
Optional<CodeRepo> codeRepo = findCodeRepoService.findCodeRepoByUrl(repoUrl);
116+
117+
return codeRepo.isPresent() && teamId.equals(codeRepo.get().getTeam().getId());
118+
} catch (Exception e) {
119+
log.error("[CodeRepo] Error checking if repo '{}' belongs to team '{}': {}", repoUrl, teamId, e.getMessage());
120+
return false;
121+
}
122+
}
123+
124+
public Boolean isValidApiKey(String apiKey, String repoUrl) {
125+
Optional<UserInfo> userOptional = userRepository.findByApiKey(apiKey);
126+
Optional<Team> codeRepoTeam = findTeamService.findByRepoUrl(repoUrl);
127+
128+
if (userOptional.isEmpty() || codeRepoTeam.isEmpty()) {
129+
return false;
130+
}
131+
132+
List<UserInfo> teamUsers = userRepository.getUsersByTeamId(codeRepoTeam.get().getId());
133+
134+
if (teamUsers.contains(userOptional.get())) {
135+
log.info("[Team Service] User's {} API key validation succeeded", userOptional.get().getUsername());
136+
return true;
137+
} else {
138+
log.info("[Team Service] User's {} API key validation failed", userOptional.get().getUsername());
139+
return false;
140+
}
141+
}
142+
143+
public String runScan(String repoUrl, String branch, String domain) {
144+
145+
CodeRepo codeRepo = findCodeRepoService.findCodeRepoByUrl(repoUrl).orElse(null);
146+
147+
CodeRepoBranch repoBranch;
148+
149+
if (branch == null) {
150+
repoBranch = codeRepo.getDefaultBranch();
151+
} else {
152+
repoBranch = codeRepo.getBranches().stream().filter(b -> b.getName().equals(branch)).findFirst().orElse(null);
153+
}
154+
155+
scanManagerService.scanRepositorySync(codeRepo, repoBranch, null, null);
156+
157+
RunOrchScanReportDto scanReport = new RunOrchScanReportDto();
158+
159+
scanReport.setRepoUrl(repoUrl);
160+
scanReport.setBranch(branch);
161+
if (domain != null) {
162+
scanReport.setLinkToScanDetails("https://" + domain + "/#/show-repo/" + codeRepo.getId());
163+
}
164+
165+
ObjectMapper mapper = new ObjectMapper();
166+
mapper.enable(SerializationFeature.INDENT_OUTPUT);
167+
168+
Collection<Finding.Status> statuses = Arrays.asList(Finding.Status.NEW, Finding.Status.EXISTING);
169+
170+
List<Finding> findings = findFindingService.findByCodeRepoAndCodeRepoBranchAndStatusIn(codeRepo, repoBranch, statuses);
171+
172+
List<RunOrchScanDetailsDto> findingsDetails = new ArrayList<>();
173+
for (Finding finding : findings) {
174+
RunOrchScanDetailsDto scanDetails = new RunOrchScanDetailsDto();
175+
176+
scanDetails.setName(finding.getVulnerability().getName());
177+
scanDetails.setDescription(finding.getVulnerability().getDescription());
178+
scanDetails.setExplanation(finding.getExplanation());
179+
scanDetails.setRecommendation(finding.getVulnerability().getRecommendation());
180+
scanDetails.setLocation(finding.getLocation());
181+
scanDetails.setSource(String.valueOf(finding.getSource()));
182+
scanDetails.setStatus(String.valueOf(finding.getStatus()));
183+
scanDetails.setSeverity(String.valueOf(finding.getSeverity()));
184+
185+
findingsDetails.add(scanDetails);
186+
}
187+
188+
scanReport.setFindings(findingsDetails);
189+
190+
try {
191+
return mapper.writeValueAsString(scanReport);
192+
} catch (Exception e) {
193+
log.error("Error while serializing scan report: {}", e.getMessage());
194+
}
195+
return null;
196+
}
104197
}

backend/src/main/java/io/mixeway/mixewayflowapi/api/gitlabcicd/service/GitLabCICDService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public CodeRepoBranch getCodeRepoBranch(String branch, CodeRepo codeRepo) {
6161
}
6262

6363
public void runCodeRepoScan(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch) {
64-
scanManagerService.scanRepositoryViaGitLabCICD(codeRepo, codeRepoBranch, null, null);
64+
scanManagerService.scanRepositorySync(codeRepo, codeRepoBranch, null, null);
6565
}
6666

6767
public String createScanSummary(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, String domain) {

backend/src/main/java/io/mixeway/mixewayflowapi/scanmanager/service/ScanManagerService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public void scanRepository(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, Str
211211
}
212212

213213
} catch (Exception e) {
214-
log.error("[ScanManagerService] Exception during scan, failed to fetch repository: {} - {}", codeRepo.getRepourl(), e.getMessage(), e);
214+
log.error("[ScanManagerService] Exception during scan, failed to fetch repository: {}", codeRepo.getRepourl());
215215
} finally {
216216
// Update status
217217
try {
@@ -243,7 +243,7 @@ public void scanRepository(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, Str
243243
});
244244
}
245245

246-
public void scanRepositoryViaGitLabCICD(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, String commitId, Long iid) {
246+
public void scanRepositorySync(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, String commitId, Long iid) {
247247
// Get or create a lock object for the repository
248248
Object repoLock = repoLocks.computeIfAbsent(codeRepo.getId(), k -> new Object());
249249

@@ -340,7 +340,7 @@ public void scanRepositoryViaGitLabCICD(CodeRepo codeRepo, CodeRepoBranch codeRe
340340
}
341341

342342
} catch (Exception e) {
343-
log.error("[ScanManagerService] Exception during scan, failed to fetch repository: {} - {}", codeRepo.getRepourl(), e.getMessage(), e);
343+
log.error("[ScanManagerService] Exception during scan, failed to fetch repository: {}", codeRepo.getRepourl());
344344
} finally {
345345
// Update status
346346
try {

0 commit comments

Comments
 (0)