Skip to content

Commit 06011a5

Browse files
authored
Merge pull request #268 from SoftwareDesignLab/dev
SVIP v8.0.5-alpha Pre-Release
2 parents 79107ee + 2fb0b80 commit 06011a5

File tree

340 files changed

+13478
-6759
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

340 files changed

+13478
-6759
lines changed

.env

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,11 @@ MYSQL_LOCAL_PORT=3306
66
MYSQL_DOCKER_PORT=3306
77

88
SPRING_LOCAL_PORT=8080
9-
SPRING_DOCKER_PORT=8080
9+
SPRING_DOCKER_PORT=8080
10+
SPRING_DOCKER_PORT=8080
11+
12+
13+
SPRING_DOCKER_PORT=8080
14+
FLASK_LOCAL_PORT=50001
15+
FLASK_DOCKER_ADDRESS=0.0.0.0
16+
FLASK_DOCKER_PORT=50001

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ app/**/*.js.map
2828
*.rar
2929

3030
# Exclude our OSI image cache
31-
!core/src/main/java/org/svip/sbomgeneration/osi/images/osi.tar.gz
31+
!core/src/main/java/org/svip/generation/osi/images/osi.tar.gz
3232

3333
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
3434
hs_err_pid*
@@ -73,4 +73,7 @@ gradle-app.setting
7373
[tT]est[mM]ain.java
7474

7575
# Mac files
76-
.DS_Store
76+
.DS_Store
77+
78+
# dev enviroment files
79+
/.dev-env/

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
> The SBOM Visualization and Integration Platform (**SVIP**) is a unified platform to promote the
33
> production, consumption, and utilization of Software Bills of Materials.
44
5-
### Latest Release: [[v7.1.2-alpha] - (7/28/2023)](doc/changelog.md)
5+
### Latest Release: [[v8.0.5-alpha] - (10/17/2023)](doc/changelog.md)
66

77
## System Requirements
88
- Java 17.X.X
@@ -55,10 +55,13 @@ Currently, SVIP Supports the following SBOM Types
5555
- [Kevin Laporte](mailto:kjl8898@rit.edu)
5656
- [Matt London](mailto:mrl2534@rit.edu)
5757
- [Dylan Mulligan](mailto:dtm5568@rit.edu)
58+
- [Amanda Nitta](mailto:nittaak@hawaii.edu)
5859

5960
**Developer Team**
61+
- [Brian Baumann](mailto:bmb5957@rit.edu)
6062
- [Asa Horn](mailto:aoh9470@rit.edu)
6163
- [Henry Keena](mailto:htk4363@rit.edu)
64+
- [Hubert Liang](mailto:hubertl@hawaii.edu)
6265
- [Ping Liu](mailto:htk4363@rit.edu)
6366
- [Henry Lu](mailto:hyl2415@rit.edu)
6467
- [Matthew Morrison](mailto:msm8275@rit.edu)
@@ -68,3 +71,4 @@ Currently, SVIP Supports the following SBOM Types
6871
- [Max Stein](mailto:mhs8558@rit.edu)
6972
- [Tom Roman](mailto:tfr8811@rit.edu)
7073
- [Liam Wilkins](mailto:ljw1484@rit.edu)
74+
- [Jordan Wong](mailto:jordanw4@hawaii.edu)

api/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies {
2727
implementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
2828
implementation 'org.mockito:mockito-core:5.2.0'
2929
implementation 'org.mockito:mockito-junit-jupiter:5.2.0'
30+
implementation 'org.springframework.boot:spring-boot-starter-test:3.1.2'
3031
testImplementation ('org.springframework.boot:spring-boot-starter-web:3.0.2')
3132
implementation('org.springframework:spring-web:6.0.7')
3233
implementation('org.springframework.boot:spring-boot-autoconfigure:3.0.4')

api/src/main/java/org/svip/api/SVIPApplication.java

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

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
6+
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
57
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
8+
import org.springframework.context.annotation.Bean;
69

710
/**
811
* This class contains the main function which runs both APIs as spring boot applications
@@ -14,4 +17,5 @@ public class SVIPApplication extends SpringBootServletInitializer {
1417
public static void main(String[] args) {
1518
SpringApplication.run(SVIPApplication.class, args);
1619
}
20+
1721
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.svip.api.controller;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.*;
9+
import org.svip.api.services.DiffService;
10+
import org.svip.api.services.SBOMFileService;
11+
12+
/**
13+
* File: DiffController.java
14+
* REST API Controller for generating Diff Reports
15+
*
16+
* @author Derek Garcia
17+
**/
18+
@RestController
19+
@RequestMapping("/svip")
20+
public class DiffController {
21+
22+
/**
23+
* Spring-configured logger
24+
*/
25+
private static final Logger LOGGER = LoggerFactory.getLogger(DiffController.class);
26+
27+
private final SBOMFileService sbomFileService;
28+
private final DiffService diffService;
29+
30+
/**
31+
* Create new Controller with services
32+
*
33+
* @param sbomService Service for handling SBOM queries
34+
* @param diffService Service for handling QA queries
35+
*/
36+
public DiffController(SBOMFileService sbomService, DiffService diffService){
37+
this.sbomFileService = sbomService;
38+
this.diffService = diffService;
39+
}
40+
41+
42+
/**
43+
* USAGE. Compares two or more given SBOMs (split into filename and contents), with the first one used as the baseline, and returns a comparison report.
44+
*
45+
* @param targetIndex the index of the target SBOM
46+
* @param ids the ids of the SBOM files
47+
* @return generated diff report
48+
* @throws JsonProcessingException Error processing the report
49+
*/
50+
@PostMapping("/sboms/compare")
51+
public ResponseEntity<String> compare(@RequestParam("targetIndex") int targetIndex, @RequestBody Long[] ids) throws JsonProcessingException {
52+
53+
try{
54+
55+
String diffReport = this.diffService.generateDiffReportAsJSON(this.sbomFileService, ids[targetIndex], ids);
56+
return new ResponseEntity<>(diffReport, HttpStatus.OK); // track status?
57+
58+
} catch (Exception e){
59+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
60+
}
61+
}
62+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package org.svip.api.controller;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.MediaType;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
import org.springframework.web.multipart.MultipartFile;
11+
import org.svip.api.entities.SBOM;
12+
import org.svip.api.requests.UploadSBOMFileInput;
13+
import org.svip.api.services.SBOMFileService;
14+
import org.svip.conversion.ConversionException;
15+
import org.svip.generation.osi.OSI;
16+
import org.svip.generation.osi.OSIClient;
17+
import org.svip.generation.osi.exceptions.DockerNotAvailableException;
18+
import org.svip.sbom.builder.SBOMBuilderException;
19+
import org.svip.serializers.SerializerFactory;
20+
import org.svip.serializers.exceptions.DeserializerException;
21+
import org.svip.serializers.exceptions.SerializerException;
22+
23+
import java.io.IOException;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
/**
29+
* REST API Controller for generating SBOMs with OSI
30+
*
31+
* @author Derek Garcia
32+
**/
33+
@RestController
34+
@RequestMapping("/svip/generators/osi")
35+
public class OSIController {
36+
/**
37+
* Spring-configured logger
38+
*/
39+
private static final Logger LOGGER = LoggerFactory.getLogger(OSIController.class);
40+
41+
private final SBOMFileService sbomService;
42+
private final OSI container;
43+
44+
/**
45+
* Create new Controller with services
46+
*
47+
* @param sbomService Service for handling SBOM queries
48+
*/
49+
public OSIController(SBOMFileService sbomService){
50+
this.sbomService = sbomService;
51+
52+
OSI container = null; // Disabled state
53+
String error = "OSI ENDPOINT DISABLED -- ";
54+
55+
try {
56+
container = new OSI();
57+
LOGGER.info("OSI ENDPOINT ENABLED");
58+
} catch (Exception e) {
59+
// If we can't construct the OSI container for any reason, log and disable OSI.
60+
LOGGER.warn(error + "Unable to setup OSI container.");
61+
LOGGER.error("OSI Docker API response: " + e.getMessage());
62+
}
63+
64+
this.container = container;
65+
}
66+
67+
/**
68+
* Public method to check if OSI is enabled on this instance of the controller.
69+
*
70+
* @return True if OSI is enabled, false otherwise.
71+
*/
72+
public static boolean isOSIEnabled() {
73+
try {
74+
return OSIClient.isOSIContainerAvailable();
75+
} catch(DockerNotAvailableException e) {
76+
return false;
77+
}
78+
}
79+
80+
///
81+
/// GET
82+
///
83+
84+
/**
85+
* USAGE. Send GET request to /generators/osi/getTools to get a list of valid tool names that can be used to
86+
* generate an SBOM from source file(s).
87+
*
88+
* @return A list of string tool names.
89+
*/
90+
@GetMapping("/tools")
91+
public ResponseEntity<?> getOSITools() {
92+
if (!isOSIEnabled())
93+
return new ResponseEntity<>("OSI has been disabled for this instance.", HttpStatus.NOT_FOUND);
94+
95+
String urlMsg = "POST /svip/generators/osi";
96+
97+
List<String> tools = container.getAllTools();
98+
if (tools == null) {
99+
LOGGER.error(urlMsg + ": " + "Error getting tool list from Docker container.");
100+
return new ResponseEntity<>("Error getting tool list from Docker container.", HttpStatus.NOT_FOUND);
101+
}
102+
103+
return new ResponseEntity<>(tools.toArray(new String[0]), HttpStatus.OK);
104+
}
105+
106+
///
107+
/// POST
108+
///
109+
110+
/**
111+
* USAGE. Send POST request to /generators/osi to generate an SBOM from source file(s).
112+
*
113+
* @param zipFile The zip file of source files to generate an SBOM from.
114+
* @param projectName The name of the project.
115+
* @param schema The schema of the desired SBOM.
116+
* @param format The file format of the desired SBOM.
117+
* @param toolNames An optional list of tool names to use when running OSI. If not provided or empty, all
118+
* possible tools will be used.
119+
* @return The ID of the uploaded SBOM.
120+
*/
121+
@PostMapping(value = "", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
122+
public ResponseEntity<?> generateOSI(@RequestPart("zipFile") MultipartFile zipFile,
123+
@RequestParam("projectName") String projectName,
124+
@RequestParam("schema") SerializerFactory.Schema schema,
125+
@RequestParam("format") SerializerFactory.Format format,
126+
@RequestParam(value = "toolNames", required = false) String[] toolNames) {
127+
if (!isOSIEnabled())
128+
return new ResponseEntity<>("OSI has been disabled for this instance.", HttpStatus.NOT_FOUND);
129+
130+
try {
131+
schema.getSerializer(format);
132+
} catch (IllegalArgumentException e) {
133+
LOGGER.error("POST /svip/generators/osi - " + e.getMessage());
134+
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
135+
}
136+
137+
Map<String, String> unZipped;
138+
try {
139+
unZipped = SBOMFileService.unZip(zipFile);
140+
} catch (IOException e) {
141+
LOGGER.error("POST /svip/generators/osi - " + e.getMessage());
142+
return new ResponseEntity<>("Make sure attachment is a zip file (.zip): " + e.getMessage(), HttpStatus.BAD_REQUEST);
143+
}
144+
145+
// Validate & add files
146+
for (Map.Entry<String, String> file : unZipped.entrySet())
147+
try {
148+
// Remove any directories, causes issues with OSI paths (unless we take in a root directory?)
149+
String fileName = file.getKey();
150+
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
151+
container.addSourceFile(fileName, file.getValue());
152+
} catch (IOException e) {
153+
LOGGER.error("POST /svip/generators/osi - Error adding source file");
154+
return new ResponseEntity<>("Error adding source file", HttpStatus.NOT_FOUND);
155+
}
156+
157+
// Generate SBOMs
158+
Map<String, String> generatedSBOMFiles;
159+
try {
160+
List<String> tools = null;
161+
if (toolNames != null && toolNames.length > 0) {
162+
tools = List.of(toolNames);
163+
LOGGER.info("POST /svip/generators/osi - Running with tool names: " + tools);
164+
} else LOGGER.info("POST /svip/generators/osi - Running with default tools");
165+
166+
generatedSBOMFiles = container.generateSBOMs(tools);
167+
} catch (Exception e) {
168+
LOGGER.warn("POST /svip/generators/osi - Exception occurred while running OSI container: " + e.getMessage());
169+
return new ResponseEntity<>("Exception occurred while running OSI container.", HttpStatus.NOT_FOUND);
170+
}
171+
172+
// Upload SBOMs
173+
List<Long> uploaded = new ArrayList<>();
174+
for (Map.Entry<String, String> sbomFile : generatedSBOMFiles.entrySet()) {
175+
UploadSBOMFileInput input = new UploadSBOMFileInput(sbomFile.getKey(), sbomFile.getValue());
176+
try {
177+
SBOM sbom = input.toSBOMFile();
178+
sbom.toSBOMObject();
179+
this.sbomService.upload(sbom);
180+
uploaded.add(sbom.getId());
181+
182+
// Log
183+
LOGGER.info("POST /svip/generators/osi - Generated SBOM with ID " + sbom.getId() + ": " + sbom.getName());
184+
} catch (IllegalArgumentException ignored) {
185+
// TODO ignore any illegal files until XML deserialization exists
186+
} catch (Exception e) {
187+
// Problem with uploading/parsing
188+
LOGGER.error("POST /svip/generators/osi - " + e.getMessage());
189+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
190+
}
191+
}
192+
193+
if (uploaded.size() < 1) {
194+
LOGGER.warn("POST /svip/generators/osi - No SBOMs generated by OSI container.");
195+
return new ResponseEntity<>("No SBOMs generated for these files.", HttpStatus.NO_CONTENT);
196+
}
197+
198+
// todo poor hotfix, need a better solution
199+
Long merged;
200+
// Only merge if 2 or more
201+
if(uploaded.size() >= 2){
202+
// Merge SBOMs into one SBOM
203+
try {
204+
merged = this.sbomService.merge(uploaded.toArray(new Long[0]));
205+
} catch (Exception e) {
206+
LOGGER.error("POST /svip/generators/osi - Unable to merge, no content: " + e.getMessage());
207+
return new ResponseEntity<>("Unable to merge, no content: " + e.getMessage(), HttpStatus.NO_CONTENT);
208+
}
209+
} else {
210+
merged = uploaded.get(0);
211+
}
212+
213+
214+
// Convert
215+
Long converted;
216+
try {
217+
converted = this.sbomService.convert(merged, schema, format, true);
218+
} catch (DeserializerException | JsonProcessingException | SerializerException | SBOMBuilderException |
219+
ConversionException e) {
220+
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
221+
}
222+
223+
// todo how to set file name using projectName
224+
225+
// Save and return
226+
return new ResponseEntity<>(converted, HttpStatus.OK);
227+
}
228+
}

0 commit comments

Comments
 (0)