Skip to content

Commit

Permalink
Merge pull request #4229 from immalla/immalla-2710
Browse files Browse the repository at this point in the history
  • Loading branch information
nscuro authored Oct 30, 2024
2 parents 6ac5c10 + 60e7d0f commit 8547447
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/main/java/org/dependencytrack/model/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public enum FetchGroup {
@Persistent
@Column(name = "CLASSIFIER", jdbcType = "VARCHAR")
@Index(name = "COMPONENT_CLASSIFIER_IDX")
@NotNull
@Extension(vendorName = "datanucleus", key = "enum-check-constraint", value = "true")
private Classifier classifier;

Expand All @@ -179,78 +180,91 @@ public enum FetchGroup {
@Persistent
@Index(name = "COMPONENT_MD5_IDX")
@Column(name = "MD5", jdbcType = "VARCHAR", length = 32)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{32}$", message = "The MD5 hash must be a valid 32 character HEX number")
private String md5;

@Persistent
@Index(name = "COMPONENT_SHA1_IDX")
@Column(name = "SHA1", jdbcType = "VARCHAR", length = 40)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{40}$", message = "The SHA1 hash must be a valid 40 character HEX number")
private String sha1;

@Persistent
@Index(name = "COMPONENT_SHA256_IDX")
@Column(name = "SHA_256", jdbcType = "VARCHAR", length = 64)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{64}$", message = "The SHA-256 hash must be a valid 64 character HEX number")
private String sha256;

@Persistent
@Index(name = "COMPONENT_SHA384_IDX")
@Column(name = "SHA_384", jdbcType = "VARCHAR", length = 96)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{96}$", message = "The SHA-384 hash must be a valid 96 character HEX number")
private String sha384;

@Persistent
@Index(name = "COMPONENT_SHA512_IDX")
@Column(name = "SHA_512", jdbcType = "VARCHAR", length = 128)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{128}$", message = "The SHA-512 hash must be a valid 128 character HEX number")
private String sha512;

@Persistent
@Index(name = "COMPONENT_SHA3_256_IDX")
@Column(name = "SHA3_256", jdbcType = "VARCHAR", length = 64)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{64}$", message = "The SHA3-256 hash must be a valid 64 character HEX number")
private String sha3_256;

@Persistent
@Index(name = "COMPONENT_SHA3_384_IDX")
@Column(name = "SHA3_384", jdbcType = "VARCHAR", length = 96)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{96}$", message = "The SHA3-384 hash must be a valid 96 character HEX number")
private String sha3_384;

@Persistent
@Index(name = "COMPONENT_SHA3_512_IDX")
@Column(name = "SHA3_512", jdbcType = "VARCHAR", length = 128)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = "^[0-9a-fA-F]{128}$", message = "The SHA3-512 hash must be a valid 128 character HEX number")
private String sha3_512;

@Persistent
@Index(name = "COMPONENT_BLAKE2B_256_IDX")
@Column(name = "BLAKE2B_256", jdbcType = "VARCHAR", length = 64)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.HASH_SHA256, message = "The BLAKE2b hash must be a valid 64 character HEX number")
private String blake2b_256;

@Persistent
@Index(name = "COMPONENT_BLAKE2B_384_IDX")
@Column(name = "BLAKE2B_384", jdbcType = "VARCHAR", length = 96)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.HASH_SHA384, message = "The BLAKE2b hash must be a valid 96 character HEX number")
private String blake2b_384;

@Persistent
@Index(name = "COMPONENT_BLAKE2B_512_IDX")
@Column(name = "BLAKE2B_512", jdbcType = "VARCHAR", length = 128)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.HASH_SHA512, message = "The BLAKE2b hash must be a valid 128 character HEX number")
private String blake2b_512;

@Persistent
@Index(name = "COMPONENT_BLAKE3_IDX")
@Column(name = "BLAKE3", jdbcType = "VARCHAR", length = 255)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.HEXADECIMAL, message = "The BLAKE3 hash must be a valid HEX number")
private String blake3;

@Persistent
@Index(name = "COMPONENT_CPE_IDX")
@Size(max = 255)
@JsonDeserialize(using = TrimmedStringDeserializer.class)
//Patterns obtained from https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd
@Pattern(regexp = "(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})", message = "The CPE must conform to the CPE v2.2 or v2.3 specification defined by NIST")
private String cpe;
Expand Down
52 changes: 51 additions & 1 deletion src/test/java/org/dependencytrack/model/ComponentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,34 @@
*/
package org.dependencytrack.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.packageurl.PackageURL;
import com.github.packageurl.PackageURLBuilder;

import jakarta.json.Json;
import jakarta.json.JsonObjectBuilder;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;

import org.dependencytrack.util.JsonUtil;
import org.junit.Assert;
import org.junit.Test;
import us.springett.parsers.cpe.Cpe;
import us.springett.parsers.cpe.CpeParser;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

public class ComponentTest {

private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

@Test
public void testId() {
Component component = new Component();
Expand Down Expand Up @@ -229,4 +244,39 @@ public void testToStringWithoutPurl() {
component.setVersion("1.0");
Assert.assertEquals("acme : product : 1.0", component.toString());
}

@Test
public void testValidEmptyValues() throws JsonProcessingException {
// test for all the String fields validated during update component
String[] fields = { "version", "group", "description", "license", "licenseExpression", "licenseUrl", "filename",
"cpe", "swidTagId", "copyright", "md5", "sha1", "sha256", "sha512", "sha3_256", "sha3_512" };

JsonObjectBuilder componentBuilder = Json.createObjectBuilder();
for (String field : fields) {
JsonUtil.add(componentBuilder, field, "");
}
String json = componentBuilder.build().toString();

ObjectMapper mapper = new ObjectMapper();
Component component = mapper.readValue(json, Component.class);

Set<ConstraintViolation<Component>> violations = null;
for (String field : fields) {
violations = validator.validateProperty(component, field);
assertTrue(violations.isEmpty());
}
}

@Test
public void testInvalidEmptyName() throws JsonProcessingException {
JsonObjectBuilder componentBuilder = Json.createObjectBuilder();
componentBuilder.add("name", "");
String json = componentBuilder.build().toString();

ObjectMapper mapper = new ObjectMapper();
Component component = mapper.readValue(json, Component.class);

Set<ConstraintViolation<Component>> violations = validator.validateProperty(component, "name");
assertFalse(violations.isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.http.HttpStatus;
import org.dependencytrack.JerseyTestRule;
import org.dependencytrack.ResourceTest;
import org.dependencytrack.model.Classifier;
import org.dependencytrack.model.Component;
import org.dependencytrack.model.ConfigPropertyConstants;
import org.dependencytrack.model.ExternalReference;
Expand Down Expand Up @@ -508,6 +509,7 @@ public void createComponentTest() {
component.setName("My Component");
component.setVersion("1.0");
component.setAuthor("SampleAuthor");
component.setClassifier(Classifier.APPLICATION);
Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid().toString()).request()
.header(X_API_KEY, apiKey)
.put(Entity.entity(component, MediaType.APPLICATION_JSON));
Expand All @@ -518,6 +520,7 @@ public void createComponentTest() {
Assert.assertEquals("SampleAuthor" ,json.getJsonArray("authors").getJsonObject(0).getString("name"));
Assert.assertEquals("SampleAuthor", json.getString("author"));
Assert.assertEquals("1.0", json.getString("version"));
Assert.assertEquals("APPLICATION", json.getString("classifier"));
Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid")));
}

Expand All @@ -528,6 +531,7 @@ public void createComponentUpperCaseHashTest() {
component.setProject(project);
component.setName("My Component");
component.setVersion("1.0");
component.setClassifier(Classifier.APPLICATION);
component.setSha1("640ab2bae07bedc4c163f679a746f7ab7fb5d1fa".toUpperCase());
component.setSha256("532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25".toUpperCase());
component.setSha3_256("c0a5cca43b8aa79eb50e3464bc839dd6fd414fae0ddf928ca23dcebf8a8b8dd0".toUpperCase());
Expand All @@ -544,6 +548,7 @@ public void createComponentUpperCaseHashTest() {
Assert.assertNotNull(json);
Assert.assertEquals("My Component", json.getString("name"));
Assert.assertEquals("1.0", json.getString("version"));
Assert.assertEquals("APPLICATION", json.getString("classifier"));
Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid")));
Assert.assertEquals(component.getSha1(), json.getString("sha1"));
Assert.assertEquals(component.getSha256(), json.getString("sha256"));
Expand Down Expand Up @@ -574,6 +579,7 @@ public void updateComponentTest() {
externalReference.setType(org.cyclonedx.model.ExternalReference.Type.WEBSITE);
externalReference.setUrl("test.com");
jsonComponent.setExternalReferences(List.of(externalReference));
jsonComponent.setClassifier(Classifier.APPLICATION);

Response response = jersey.target(V1_COMPONENT).request()
.header(X_API_KEY, apiKey)
Expand Down Expand Up @@ -626,6 +632,7 @@ public void updateComponentInvalidLicenseExpressionTest() {
"uuid": "%s",
"name": "acme-lib",
"version": "1.0.0",
"classifier":"APPLICATION",
"licenseExpression": "(invalid"
}
""".formatted(component.getUuid()), MediaType.APPLICATION_JSON_TYPE));
Expand Down

0 comments on commit 8547447

Please sign in to comment.