Skip to content

Commit 02f66d6

Browse files
Use Snyk as data provider (#860)
Fixes #717
1 parent 8b7bbd7 commit 02f66d6

35 files changed

+1043
-138
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.sap.oss.phosphor.fosstars.advice.oss;
2+
3+
import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK;
4+
5+
import com.sap.oss.phosphor.fosstars.advice.Advice;
6+
import com.sap.oss.phosphor.fosstars.advice.oss.OssAdviceContentYamlStorage.OssAdviceContext;
7+
import com.sap.oss.phosphor.fosstars.model.Subject;
8+
import com.sap.oss.phosphor.fosstars.model.Value;
9+
import com.sap.oss.phosphor.fosstars.model.score.oss.SnykDependencyScanScore;
10+
import com.sap.oss.phosphor.fosstars.model.value.ScoreValue;
11+
import java.net.MalformedURLException;
12+
import java.util.Collections;
13+
import java.util.List;
14+
import java.util.Optional;
15+
16+
/**
17+
* An advisor for features related to Snyk.
18+
*/
19+
public class SnykAdvisor extends AbstractOssAdvisor {
20+
21+
/**
22+
* Create a new advisor.
23+
*
24+
* @param contextFactory A factory that provides contexts for advice.
25+
*/
26+
public SnykAdvisor(OssAdviceContextFactory contextFactory) {
27+
super(OssAdviceContentYamlStorage.DEFAULT, contextFactory);
28+
}
29+
30+
@Override
31+
protected List<Advice> adviceFor(
32+
Subject subject, List<Value<?>> usedValues, OssAdviceContext context)
33+
throws MalformedURLException {
34+
35+
Optional<ScoreValue> snykScore = findSubScoreValue(subject, SnykDependencyScanScore.class);
36+
37+
if (!snykScore.isPresent() || snykScore.get().isNotApplicable()) {
38+
return Collections.emptyList();
39+
}
40+
41+
return adviceForBooleanFeature(usedValues, USES_SNYK, subject, context);
42+
}
43+
}

src/main/java/com/sap/oss/phosphor/fosstars/data/DataProviderSelector.java

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.sap.oss.phosphor.fosstars.data.github.UsesOwaspDependencyCheck;
5757
import com.sap.oss.phosphor.fosstars.data.github.UsesSanitizers;
5858
import com.sap.oss.phosphor.fosstars.data.github.UsesSignedCommits;
59+
import com.sap.oss.phosphor.fosstars.data.github.UsesSnyk;
5960
import com.sap.oss.phosphor.fosstars.data.github.VulnerabilityAlertsInfo;
6061
import com.sap.oss.phosphor.fosstars.data.interactive.AskAboutSecurityTeam;
6162
import com.sap.oss.phosphor.fosstars.data.interactive.AskAboutUnpatchedVulnerabilities;
@@ -210,6 +211,7 @@ public DataProviderSelector(GitHubDataFetcher fetcher, NVD nvd) throws IOExcepti
210211
new LgtmDataProvider(fetcher),
211212
new UsesSignedCommits(fetcher),
212213
new UsesDependabot(fetcher),
214+
new UsesSnyk(fetcher),
213215
new ProgrammingLanguages(fetcher),
214216
new PackageManagement(fetcher),
215217
new UsesNoHttpTool(fetcher),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.sap.oss.phosphor.fosstars.data.github;
2+
3+
import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject;
4+
import java.io.IOException;
5+
import java.time.Duration;
6+
import java.time.Instant;
7+
import java.util.Date;
8+
import java.util.Optional;
9+
import org.kohsuke.github.GHIssueState;
10+
import org.kohsuke.github.GHPullRequest;
11+
import org.kohsuke.github.GHUser;
12+
13+
/**
14+
* This is a base class for dependency checker data providers such as Dependabot and Snyk.
15+
*/
16+
public abstract class AbstractDependencyScanDataProvider extends GitHubCachingDataProvider {
17+
18+
/**
19+
* Period of time to be checked.
20+
*/
21+
private static final Duration ONE_YEAR = Duration.ofDays(365);
22+
23+
/**
24+
* A minimal number of characters in a config for dependency checker.
25+
*/
26+
private static final int ACCEPTABLE_CONFIG_SIZE = 10;
27+
28+
protected abstract String getDependencyCheckerPattern();
29+
30+
/**
31+
* Initializes a data provider.
32+
*
33+
* @param fetcher An interface to GitHub.
34+
*/
35+
public AbstractDependencyScanDataProvider(
36+
GitHubDataFetcher fetcher) {
37+
super(fetcher);
38+
}
39+
40+
/**
41+
* Checks if a repository contains commits from dependency checker in the commit history.
42+
*
43+
* @param repository The repository.
44+
* @return True if at least one commit from dependency checker was found, false otherwise.
45+
*/
46+
public boolean hasDependencyCheckerCommits(LocalRepository repository) {
47+
Date date = Date.from(Instant.now().minus(ONE_YEAR));
48+
49+
try {
50+
for (Commit commit : repository.commitsAfter(date)) {
51+
if (isDependencyChecker(commit)) {
52+
return true;
53+
}
54+
}
55+
} catch (IOException e) {
56+
logger.warn("Something went wrong!", e);
57+
}
58+
59+
return false;
60+
}
61+
62+
/**
63+
* Checks if a repository has a configuration file for dependency checker.
64+
*
65+
* @param repository The repository
66+
* @param configs The config files path as String array
67+
* @return True if a config was found, false otherwise.
68+
* @throws IOException If something went wrong.
69+
*/
70+
public boolean hasDependencyCheckerConfig(LocalRepository repository, String[] configs)
71+
throws IOException {
72+
for (String config : configs) {
73+
Optional<String> content = repository.file(config);
74+
if (content.isPresent() && content.get().length() >= ACCEPTABLE_CONFIG_SIZE) {
75+
return true;
76+
}
77+
}
78+
79+
return false;
80+
}
81+
82+
/**
83+
* Checks whether a project has open pull requests from dependency checker.
84+
*
85+
* @param project The project.
86+
* @return True if the project has open pull requests form dependency checker.
87+
* @throws IOException If something went wrong.
88+
*/
89+
public boolean hasOpenPullRequestFromDependencyChecker(GitHubProject project) throws IOException {
90+
return fetcher.repositoryFor(project).getPullRequests(GHIssueState.OPEN).stream()
91+
.anyMatch(this::createdByDependencyChecker);
92+
}
93+
94+
/**
95+
* Checks if a pull request was created by dependency checker.
96+
*
97+
* @param pullRequest The pull request.
98+
* @return True if the user looks like dependency checker, false otherwise.
99+
*/
100+
private boolean createdByDependencyChecker(GHPullRequest pullRequest) {
101+
try {
102+
GHUser user = pullRequest.getUser();
103+
return isDependencyChecker(user.getName()) || isDependencyChecker(user.getLogin());
104+
} catch (IOException e) {
105+
logger.warn("Oops! Could not fetch name or login!", e);
106+
return false;
107+
}
108+
}
109+
110+
/**
111+
* Checks if a commit was done by dependency checker.
112+
*
113+
* @param commit The commit to be checked.
114+
* @return True if the commit was done by dependency checker, false otherwise.
115+
*/
116+
private boolean isDependencyChecker(Commit commit) {
117+
if (isDependencyChecker(commit.authorName()) || isDependencyChecker(commit.committerName())) {
118+
return true;
119+
}
120+
121+
for (String line : commit.message()) {
122+
if ((line.startsWith("Signed-off-by:") || line.startsWith("Co-authored-by:"))
123+
&& line.contains(getDependencyCheckerPattern())) {
124+
return true;
125+
}
126+
}
127+
128+
return false;
129+
}
130+
131+
/**
132+
* Checks whether a name looks like dependency checker.
133+
*
134+
* @param name The name.
135+
* @return True if the name looks like dependency checker, false otherwise.
136+
*/
137+
private boolean isDependencyChecker(String name) {
138+
return name != null && name.toLowerCase().contains(getDependencyCheckerPattern());
139+
}
140+
}

src/main/java/com/sap/oss/phosphor/fosstars/data/github/HasExecutableBinaries.java

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import java.nio.file.Path;
1111
import java.util.Arrays;
1212
import java.util.List;
13-
import org.kohsuke.github.GitHub;
14-
import org.kohsuke.github.GitHubBuilder;
1513

1614
/**
1715
* The data provider tries to figure out if an open-source project has executable binaries (for

src/main/java/com/sap/oss/phosphor/fosstars/data/github/PackageManagement.java

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS;
55
import static com.sap.oss.phosphor.fosstars.model.value.Language.C_SHARP;
66
import static com.sap.oss.phosphor.fosstars.model.value.Language.F_SHARP;
7+
import static com.sap.oss.phosphor.fosstars.model.value.Language.GO;
78
import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVA;
89
import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVASCRIPT;
910
import static com.sap.oss.phosphor.fosstars.model.value.Language.PHP;
@@ -13,6 +14,7 @@
1314
import static com.sap.oss.phosphor.fosstars.model.value.Language.VISUALBASIC;
1415
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.COMPOSER;
1516
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.DOTNET;
17+
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GOMODULES;
1618
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GRADLE;
1719
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.MAVEN;
1820
import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.NPM;
@@ -63,6 +65,7 @@ public class PackageManagement extends CachedSingleFeatureGitHubDataProvider<Pac
6365
register(PYTHON, PIP);
6466
register(RUBY, RUBYGEMS);
6567
register(PHP, COMPOSER);
68+
register(GO, GOMODULES);
6669
}
6770

6871
/**
@@ -83,6 +86,7 @@ public class PackageManagement extends CachedSingleFeatureGitHubDataProvider<Pac
8386
".vcxproj"::equals, ".fsproj"::equals, "packages.config"::equals);
8487
register(RUBYGEMS, "Gemfile.lock"::equals, "Gemfile"::equals, ".gemspec"::endsWith);
8588
register(COMPOSER, "composer.json"::equals, "composer.lock"::equals);
89+
register(GOMODULES, "go.mod"::equals, "go.sum"::equals);
8690
}
8791

8892
/**

0 commit comments

Comments
 (0)