Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support of deprecation of Nuget and Npm #4521

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,21 @@
*/
package org.dependencytrack.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Date;

import jakarta.validation.constraints.NotNull;
import javax.jdo.annotations.Column;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Index;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import java.io.Serializable;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

/**
* Tracks third-party metadata about component groups from external repositories
Expand Down Expand Up @@ -81,6 +83,21 @@ public class RepositoryMetaComponent implements Serializable {
@NotNull
private String latestVersion;

/**
* Whether the component is deprecated or not.
*/
@Persistent
@Column(name = "IS_DEPRECATED", allowsNull = "false", defaultValue="false")
@NotNull
private boolean isDeprecated; // Added in 4.13.0

/**
* Whether the component is deprecated or not.
*/
@Persistent
@Column(name = "DEPRECATION_MESSAGE", allowsNull = "true")
private String deprecationMessage; // Added in 4.13.0

/**
* The optional date when the component was last published.
*/
Expand Down Expand Up @@ -140,6 +157,22 @@ public void setLatestVersion(String latestVersion) {
this.latestVersion = latestVersion;
}

public boolean isDeprecated() {
return isDeprecated;
}

public void setDeprecated(boolean deprecated) {
isDeprecated = deprecated;
}

public String getDeprecationMessage() {
return deprecationMessage;
}

public void setDeprecationMessage(String deprecationMessage) {
this.deprecationMessage = deprecationMessage;
}

public Date getPublished() {
return published;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public PaginatedResult getComponents(final Project project, final boolean includ
" SELECT FROM org.dependencytrack.model.RepositoryMetaComponent m " +
" WHERE m.name == this.name " +
" && (m.namespace == this.group || (m.namespace == null && this.group == null)) " +
" && m.latestVersion != this.version " +
" && (m.latestVersion != this.version || m.isDeprecated)" +
" && this.purl.matches('pkg:' + m.repositoryType.toString().toLowerCase() + '/%') " +
" ).isEmpty()";
}
Expand Down Expand Up @@ -579,6 +579,8 @@ public Map<String, Component> getDependencyGraphForComponents(Project project, L
if (repoMetaComponent != null) {
RepositoryMetaComponent transientRepoMetaComponent = new RepositoryMetaComponent();
transientRepoMetaComponent.setLatestVersion(repoMetaComponent.getLatestVersion());
transientRepoMetaComponent.setDeprecated(repoMetaComponent.isDeprecated());
transientRepoMetaComponent.setDeprecationMessage(repoMetaComponent.getDeprecationMessage());
transientComponent.setRepositoryMeta(transientRepoMetaComponent);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@
*/
package org.dependencytrack.persistence;

import alpine.resources.AlpineRequest;
import com.github.packageurl.PackageURL;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.dependencytrack.model.Analysis;
import org.dependencytrack.model.AnalysisComment;
import org.dependencytrack.model.AnalysisJustification;
Expand All @@ -35,14 +43,9 @@
import org.dependencytrack.persistence.RepositoryQueryManager.RepositoryMetaComponentSearch;
import org.dependencytrack.util.PurlUtil;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.github.packageurl.PackageURL;

import alpine.resources.AlpineRequest;

public class FindingsQueryManager extends QueryManager implements IQueryManager {

Expand Down Expand Up @@ -331,6 +334,8 @@ public List<Finding> getFindings(Project project, boolean includeSuppressed) {
if (affectedFindings != null) {
for (final Finding finding : affectedFindings) {
finding.getComponent().put("latestVersion", metaComponent.getLatestVersion());
finding.getComponent().put("isDeprecated", metaComponent.isDeprecated());
finding.getComponent().put("deprecationMessage", metaComponent.getDeprecationMessage());
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public PaginatedResult getAllFindings(final Map<String, String> filters, final b
final RepositoryMetaComponent repoMetaComponent = getRepositoryMetaComponent(type, purl.getNamespace(), purl.getName());
if (repoMetaComponent != null) {
finding.getComponent().put("latestVersion", repoMetaComponent.getLatestVersion());
finding.getComponent().put("isDeprecated", repoMetaComponent.isDeprecated());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@
*/
package org.dependencytrack.persistence;

import alpine.common.logging.Logger;
import alpine.persistence.PaginatedResult;
import alpine.resources.AlpineRequest;
import alpine.security.crypto.DataEncryption;
import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.model.Repository;
import org.dependencytrack.model.RepositoryMetaComponent;
import org.dependencytrack.model.RepositoryType;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -37,6 +26,19 @@
import java.util.Map;
import java.util.UUID;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.model.Repository;
import org.dependencytrack.model.RepositoryMetaComponent;
import org.dependencytrack.model.RepositoryType;

import alpine.common.logging.Logger;
import alpine.persistence.PaginatedResult;
import alpine.resources.AlpineRequest;
import alpine.security.crypto.DataEncryption;

public class RepositoryQueryManager extends QueryManager implements IQueryManager {
private static final Logger LOGGER = Logger.getLogger(RepositoryQueryManager.class);

Expand Down Expand Up @@ -241,6 +243,8 @@ public synchronized RepositoryMetaComponent synchronizeRepositoryMetaComponent(
metaComponent.setNamespace(transientRepositoryMetaComponent.getNamespace());
metaComponent.setLastCheck(transientRepositoryMetaComponent.getLastCheck());
metaComponent.setLatestVersion(transientRepositoryMetaComponent.getLatestVersion());
metaComponent.setDeprecated(transientRepositoryMetaComponent.isDeprecated());
metaComponent.setDeprecationMessage(transientRepositoryMetaComponent.getDeprecationMessage());
metaComponent.setName(transientRepositoryMetaComponent.getName());
metaComponent.setPublished(transientRepositoryMetaComponent.getPublished());
return persist(metaComponent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
*/
package org.dependencytrack.tasks.repositories;

import org.dependencytrack.model.Component;

import java.util.Date;

import org.dependencytrack.model.Component;

public class MetaModel {

private final Component component;
private String latestVersion;
private boolean isDeprecated;
private String deprecationMessage;
private Date publishedTimestamp;

public MetaModel(final Component component) {
Expand All @@ -44,6 +46,22 @@ public void setLatestVersion(final String latestVersion) {
this.latestVersion = latestVersion;
}

public boolean isDeprecated() {
return isDeprecated;
}

public void setDeprecated(boolean isDeprecated) {
this.isDeprecated = isDeprecated;
}

public String getDeprecationMessage() {
return deprecationMessage;
}

public void setDeprecationMessage(final String deprecationMessage) {
this.deprecationMessage = deprecationMessage;
}

public Date getPublishedTimestamp() {
return publishedTimestamp;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
*/
package org.dependencytrack.tasks.repositories;

import alpine.common.logging.Logger;
import com.github.packageurl.PackageURL;
import java.io.IOException;

import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
Expand All @@ -28,7 +28,9 @@
import org.dependencytrack.model.RepositoryType;
import org.json.JSONObject;

import java.io.IOException;
import com.github.packageurl.PackageURL;

import alpine.common.logging.Logger;

/**
* An IMetaAnalyzer implementation that supports NPM.
Expand All @@ -40,7 +42,8 @@ public class NpmMetaAnalyzer extends AbstractMetaAnalyzer {

private static final Logger LOGGER = Logger.getLogger(NpmMetaAnalyzer.class);
private static final String DEFAULT_BASE_URL = "https://registry.npmjs.org";
private static final String API_URL = "/-/package/%s/dist-tags";
private static final String VERSION_URL = "/-/package/%s/dist-tags";
private static final String PACKAGE_URL = "/%s/%s";

NpmMetaAnalyzer() {
this.baseUrl = DEFAULT_BASE_URL;
Expand Down Expand Up @@ -74,8 +77,9 @@ public MetaModel analyze(final Component component) {
packageName = component.getPurl().getName();
}

final String url = String.format(baseUrl + API_URL, urlEncode(packageName));
try (final CloseableHttpResponse response = processHttpRequest(url)) {
// Get the latest version
final String versionUrl = String.format(baseUrl + VERSION_URL, urlEncode(packageName));
try (final CloseableHttpResponse response = processHttpRequest(versionUrl)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
if (response.getEntity()!=null) {
String responseString = EntityUtils.toString(response.getEntity());
Expand All @@ -86,13 +90,37 @@ public MetaModel analyze(final Component component) {
}
}
} else {
handleUnexpectedHttpResponse(LOGGER, url, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), component);
handleUnexpectedHttpResponse(LOGGER, versionUrl, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), component);
}
} catch (IOException e) {
handleRequestException(LOGGER, e);
} catch (Exception ex) {
throw new MetaAnalyzerException(ex);
}

// Get deprecation information
if (meta.getLatestVersion() != null && !meta.getLatestVersion().isEmpty()) {
final String packageUrl = String.format(baseUrl + PACKAGE_URL, urlEncode(packageName), urlEncode(meta.getLatestVersion()));
try (final CloseableHttpResponse response = processHttpRequest(packageUrl)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
if (response.getEntity()!=null) {
String responseString = EntityUtils.toString(response.getEntity());
var jsonObject = new JSONObject(responseString);
final String deprecated = jsonObject.optString("deprecated");
if (deprecated != null && deprecated.length() > 0) {
meta.setDeprecated(true);
meta.setDeprecationMessage(deprecated);
}
}
} else {
handleUnexpectedHttpResponse(LOGGER, packageUrl, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), component);
}
} catch (IOException e) {
handleRequestException(LOGGER, e);
} catch (Exception ex) {
throw new MetaAnalyzerException(ex);
}
}
}
return meta;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
*/
package org.dependencytrack.tasks.repositories;

import alpine.common.logging.Logger;
import com.github.packageurl.PackageURL;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
Expand All @@ -30,11 +34,9 @@
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.github.packageurl.PackageURL;

import alpine.common.logging.Logger;

/**
* An IMetaAnalyzer implementation that supports Nuget.
Expand Down Expand Up @@ -164,11 +166,32 @@
if (response.getEntity() != null) {
String stringResponse = EntityUtils.toString(response.getEntity());
if (!stringResponse.equalsIgnoreCase("") && !stringResponse.equalsIgnoreCase("{}")) {
JSONObject jsonResponse = new JSONObject(stringResponse);
final JSONObject jsonResponse = new JSONObject(stringResponse);
final String updateTime = jsonResponse.optString("published", null);
if (updateTime != null) {
meta.setPublishedTimestamp(parseUpdateTime(updateTime));
}

final String catalogEntry = jsonResponse.optString("catalogEntry", null);

try (final CloseableHttpResponse catalogResponse = processHttpRequest(catalogEntry)) {
if (catalogResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
if (catalogResponse.getEntity() != null) {

Check warning on line 179 in src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java#L179

Deeply nested if..then statements are hard to read
final String responseString = EntityUtils.toString(catalogResponse.getEntity());
final JSONObject responseJson = new JSONObject(responseString);
final JSONObject deprecationObject = responseJson.optJSONObject("deprecation");
if (deprecationObject != null) {
meta.setDeprecated(deprecationObject.optJSONArray("reasons") != null);
meta.setDeprecationMessage(deprecationObject.optString("message"));
}
}
}
} catch (IOException e) {
handleRequestException(LOGGER, e);
} catch (Exception ex) {
throw new MetaAnalyzerException(ex);
}

return true;
}
}
Expand Down
Loading