Skip to content

Commit

Permalink
Use Snyk as data provider (SAP#860)
Browse files Browse the repository at this point in the history
Fixes SAP#717
  • Loading branch information
ManjunathMS35 authored and sourabhsparkala committed Aug 31, 2022
1 parent 05c8d1c commit 2f63b5a
Show file tree
Hide file tree
Showing 17 changed files with 184 additions and 275 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.sap.oss.phosphor.fosstars.data.github;

import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHUser;

/**
* This is a base class for dependency checker data providers such as Dependabot and Snyk.
*/
public abstract class AbstractDependencyScanDataProvider extends GitHubCachingDataProvider {

/**
* Period of time to be checked.
*/
private static final Duration ONE_YEAR = Duration.ofDays(365);

/**
* A minimal number of characters in a config for dependency checker.
*/
private static final int ACCEPTABLE_CONFIG_SIZE = 10;

protected abstract String getDependencyCheckerPattern();

/**
* Initializes a data provider.
*
* @param fetcher An interface to GitHub.
*/
public AbstractDependencyScanDataProvider(
GitHubDataFetcher fetcher) {
super(fetcher);
}

/**
* Checks if a repository contains commits from dependency checker in the commit history.
*
* @param repository The repository.
* @return True if at least one commit from dependency checker was found, false otherwise.
*/
public boolean hasDependencyCheckerCommits(LocalRepository repository) {
Date date = Date.from(Instant.now().minus(ONE_YEAR));

try {
for (Commit commit : repository.commitsAfter(date)) {
if (isDependencyChecker(commit)) {
return true;
}
}
} catch (IOException e) {
logger.warn("Something went wrong!", e);
}

return false;
}

/**
* Checks if a repository has a configuration file for dependency checker.
*
* @param repository The repository
* @param configs The config files path as String array
* @return True if a config was found, false otherwise.
* @throws IOException If something went wrong.
*/
public boolean hasDependencyCheckerConfig(LocalRepository repository, String[] configs)
throws IOException {
for (String config : configs) {
Optional<String> content = repository.file(config);
if (content.isPresent() && content.get().length() >= ACCEPTABLE_CONFIG_SIZE) {
return true;
}
}

return false;
}

/**
* Checks whether a project has open pull requests from dependency checker.
*
* @param project The project.
* @return True if the project has open pull requests form dependency checker.
* @throws IOException If something went wrong.
*/
public boolean hasOpenPullRequestFromDependencyChecker(GitHubProject project) throws IOException {
return fetcher.repositoryFor(project).getPullRequests(GHIssueState.OPEN).stream()
.anyMatch(this::createdByDependencyChecker);
}

/**
* Checks if a pull request was created by dependency checker.
*
* @param pullRequest The pull request.
* @return True if the user looks like dependency checker, false otherwise.
*/
private boolean createdByDependencyChecker(GHPullRequest pullRequest) {
try {
GHUser user = pullRequest.getUser();
return isDependencyChecker(user.getName()) || isDependencyChecker(user.getLogin());
} catch (IOException e) {
logger.warn("Oops! Could not fetch name or login!", e);
return false;
}
}

/**
* Checks if a commit was done by dependency checker.
*
* @param commit The commit to be checked.
* @return True if the commit was done by dependency checker, false otherwise.
*/
private boolean isDependencyChecker(Commit commit) {
if (isDependencyChecker(commit.authorName()) || isDependencyChecker(commit.committerName())) {
return true;
}

for (String line : commit.message()) {
if ((line.startsWith("Signed-off-by:") || line.startsWith("Co-authored-by:"))
&& line.contains(getDependencyCheckerPattern())) {
return true;
}
}

return false;
}

/**
* Checks whether a name looks like dependency checker.
*
* @param name The name.
* @return True if the name looks like dependency checker, false otherwise.
*/
private boolean isDependencyChecker(String name) {
return name != null && name.toLowerCase().contains(getDependencyCheckerPattern());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;

/**
* The data provider tries to figure out if an open-source project has executable binaries (for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public class PackageManagement extends CachedSingleFeatureGitHubDataProvider<Pac
".vcxproj"::equals, ".fsproj"::equals, "packages.config"::equals);
register(RUBYGEMS, "Gemfile.lock"::equals, "Gemfile"::equals, ".gemspec"::endsWith);
register(COMPOSER, "composer.json"::equals, "composer.lock"::equals);
register(GOMODULES, "go.mod"::equals);
register(GOMODULES, "go.mod"::equals, "go.sum"::equals);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@
import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject;
import com.sap.oss.phosphor.fosstars.model.value.ValueHashSet;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import java.util.Set;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHUser;

/**
* <p>This data provider checks if an open-source project on GitHub
Expand All @@ -28,7 +21,7 @@
* Next, the provider searches for commits from Dependabot in the commit history.
* If the commits are found, then the provider also reports that the project uses Dependabot.</p>
*/
public class UsesDependabot extends GitHubCachingDataProvider {
public class UsesDependabot extends AbstractDependencyScanDataProvider {

/**
* A list of locations of a Dependabot configuration file in a repository.
Expand All @@ -41,20 +34,15 @@ public class UsesDependabot extends GitHubCachingDataProvider {
".github/dependabot.yml"
};

/**
* A minimal number of characters in a config for Dependabot.
*/
private static final int ACCEPTABLE_CONFIG_SIZE = 10;

/**
* A pattern to detect commits by Dependabot.
*/
private static final String DEPENDABOT_PATTERN = "dependabot";

/**
* Period of time to be checked.
*/
private static final Duration ONE_YEAR = Duration.ofDays(365);
@Override
protected String getDependencyCheckerPattern() {
return DEPENDABOT_PATTERN;
}

/**
* Initializes a data provider.
Expand All @@ -77,105 +65,10 @@ protected ValueSet fetchValuesFor(GitHubProject project) throws IOException {
LocalRepository repository = GitHubDataFetcher.localRepositoryFor(project);

return ValueHashSet.from(
USES_DEPENDABOT.value(hasDependabotConfig(repository) || hasDependabotCommits(repository)),
HAS_OPEN_PULL_REQUEST_FROM_DEPENDABOT.value(hasOpenPullRequestFromDependabot(project)));
}

/**
* Checks if a repository contains commits from Dependabot in the commit history.
*
* @param repository The repository.
* @return True if at least one commit from Dependabot was found, false otherwise.
*/
private boolean hasDependabotCommits(LocalRepository repository) {
Date date = Date.from(Instant.now().minus(ONE_YEAR));

try {
for (Commit commit : repository.commitsAfter(date)) {
if (isDependabot(commit)) {
return true;
}
}
} catch (IOException e) {
logger.warn("Something went wrong!", e);
}

return false;
}

/**
* Checks if a repository has a configuration file for Dependabot.
*
* @param repository The repository
* @return True if a config was found, false otherwise.
*/
private boolean hasDependabotConfig(LocalRepository repository) throws IOException {
for (String config : DEPENDABOT_CONFIGS) {
Optional<String> content = repository.file(config);
if (content.isPresent() && content.get().length() >= ACCEPTABLE_CONFIG_SIZE) {
return true;
}
}

return false;
}

/**
* Checks whether a project has open pull requests from Dependabot.
*
* @param project The project.
* @return True if the project has open pull requests form Dependabot.
* @throws IOException If something went wrong.
*/
private boolean hasOpenPullRequestFromDependabot(GitHubProject project) throws IOException {
return fetcher.repositoryFor(project).getPullRequests(GHIssueState.OPEN).stream()
.anyMatch(this::createdByDependabot);
}

/**
* Checks if a pull request was created by Dependabot.
*
* @param pullRequest The pull request.
* @return True if the user looks like Dependabot, false otherwise.
*/
private boolean createdByDependabot(GHPullRequest pullRequest) {
try {
GHUser user = pullRequest.getUser();
return isDependabot(user.getName()) || isDependabot(user.getLogin());
} catch (IOException e) {
logger.warn("Oops! Could not fetch name or login!", e);
return false;
}
}

/**
* Checks if a commit was done by Dependabot.
*
* @param commit The commit to be checked.
* @return True if the commit was done by Dependabot, false otherwise.
*/
private static boolean isDependabot(Commit commit) {
if (isDependabot(commit.authorName()) || isDependabot(commit.committerName())) {
return true;
}

for (String line : commit.message()) {
if ((line.startsWith("Signed-off-by:") || line.startsWith("Co-authored-by:"))
&& line.contains(DEPENDABOT_PATTERN)) {
return true;
}
}

return false;
}

/**
* Checks whether a name looks like Dependabot.
*
* @param name The name.
* @return True if the name looks like Dependabot, false otherwise.
*/
private static boolean isDependabot(String name) {
return name != null && name.toLowerCase().contains(DEPENDABOT_PATTERN);
USES_DEPENDABOT.value(
hasDependencyCheckerConfig(repository, DEPENDABOT_CONFIGS)
|| hasDependencyCheckerCommits(repository)),
HAS_OPEN_PULL_REQUEST_FROM_DEPENDABOT.value(
hasOpenPullRequestFromDependencyChecker(project)));
}
}
Loading

0 comments on commit 2f63b5a

Please sign in to comment.