diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisor.java b/src/main/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisor.java new file mode 100644 index 000000000..e5485a654 --- /dev/null +++ b/src/main/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisor.java @@ -0,0 +1,43 @@ +package com.sap.oss.phosphor.fosstars.advice.oss; + +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; + +import com.sap.oss.phosphor.fosstars.advice.Advice; +import com.sap.oss.phosphor.fosstars.advice.oss.OssAdviceContentYamlStorage.OssAdviceContext; +import com.sap.oss.phosphor.fosstars.model.Subject; +import com.sap.oss.phosphor.fosstars.model.Value; +import com.sap.oss.phosphor.fosstars.model.score.oss.SnykDependencyScanScore; +import com.sap.oss.phosphor.fosstars.model.value.ScoreValue; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * An advisor for features related to Snyk. + */ +public class SnykAdvisor extends AbstractOssAdvisor { + + /** + * Create a new advisor. + * + * @param contextFactory A factory that provides contexts for advice. + */ + public SnykAdvisor(OssAdviceContextFactory contextFactory) { + super(OssAdviceContentYamlStorage.DEFAULT, contextFactory); + } + + @Override + protected List adviceFor( + Subject subject, List> usedValues, OssAdviceContext context) + throws MalformedURLException { + + Optional snykScore = findSubScoreValue(subject, SnykDependencyScanScore.class); + + if (!snykScore.isPresent() || snykScore.get().isNotApplicable()) { + return Collections.emptyList(); + } + + return adviceForBooleanFeature(usedValues, USES_SNYK, subject, context); + } +} diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/data/DataProviderSelector.java b/src/main/java/com/sap/oss/phosphor/fosstars/data/DataProviderSelector.java index cf9190a8d..956f1e586 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/data/DataProviderSelector.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/data/DataProviderSelector.java @@ -56,6 +56,7 @@ import com.sap.oss.phosphor.fosstars.data.github.UsesOwaspDependencyCheck; import com.sap.oss.phosphor.fosstars.data.github.UsesSanitizers; import com.sap.oss.phosphor.fosstars.data.github.UsesSignedCommits; +import com.sap.oss.phosphor.fosstars.data.github.UsesSnyk; import com.sap.oss.phosphor.fosstars.data.github.VulnerabilityAlertsInfo; import com.sap.oss.phosphor.fosstars.data.interactive.AskAboutSecurityTeam; import com.sap.oss.phosphor.fosstars.data.interactive.AskAboutUnpatchedVulnerabilities; @@ -210,6 +211,7 @@ public DataProviderSelector(GitHubDataFetcher fetcher, NVD nvd) throws IOExcepti new LgtmDataProvider(fetcher), new UsesSignedCommits(fetcher), new UsesDependabot(fetcher), + new UsesSnyk(fetcher), new ProgrammingLanguages(fetcher), new PackageManagement(fetcher), new UsesNoHttpTool(fetcher), diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/AbstractDependencyScanDataProvider.java b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/AbstractDependencyScanDataProvider.java new file mode 100644 index 000000000..19ec932e3 --- /dev/null +++ b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/AbstractDependencyScanDataProvider.java @@ -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 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()); + } +} diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/HasExecutableBinaries.java b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/HasExecutableBinaries.java index 74861e003..ddc61abaf 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/HasExecutableBinaries.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/HasExecutableBinaries.java @@ -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 diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/PackageManagement.java b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/PackageManagement.java index 9a5b370e2..bd18147f7 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/PackageManagement.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/PackageManagement.java @@ -4,6 +4,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS; import static com.sap.oss.phosphor.fosstars.model.value.Language.C_SHARP; import static com.sap.oss.phosphor.fosstars.model.value.Language.F_SHARP; +import static com.sap.oss.phosphor.fosstars.model.value.Language.GO; import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVA; import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVASCRIPT; import static com.sap.oss.phosphor.fosstars.model.value.Language.PHP; @@ -13,6 +14,7 @@ import static com.sap.oss.phosphor.fosstars.model.value.Language.VISUALBASIC; import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.COMPOSER; import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.DOTNET; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GOMODULES; import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GRADLE; import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.MAVEN; import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.NPM; @@ -63,6 +65,7 @@ public class PackageManagement extends CachedSingleFeatureGitHubDataProviderThis data provider checks if an open-source project on GitHub @@ -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.

*/ -public class UsesDependabot extends GitHubCachingDataProvider { +public class UsesDependabot extends AbstractDependencyScanDataProvider { /** * A list of locations of a Dependabot configuration file in a repository. @@ -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. @@ -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 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))); } } \ No newline at end of file diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnyk.java b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnyk.java new file mode 100644 index 000000000..98f8fb85d --- /dev/null +++ b/src/main/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnyk.java @@ -0,0 +1,106 @@ +package com.sap.oss.phosphor.fosstars.data.github; + +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_OPEN_PULL_REQUEST_FROM_SNYK; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; +import static com.sap.oss.phosphor.fosstars.model.other.Utils.setOf; + +import com.sap.oss.phosphor.fosstars.model.Feature; +import com.sap.oss.phosphor.fosstars.model.ValueSet; +import com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures; +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.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +/** + * This data provider checks if an open-source project on GitHub uses Snyk, and fills out the {@link + * OssFeatures#USES_SNYK} feature. + * + *

First, the provider checks if a repository contains a policy file for Snyk. If the policy file + * exists, then the provider reports that the project uses Snyk. Next, the provider searches for + * commits from Snyk in the commit history. If the commits are found, then the provider also reports + * that the project uses Snyk. + */ +public class UsesSnyk extends AbstractDependencyScanDataProvider { + + /** + * A file name containing Snyk policies in a repository. + * + * @see The .snyk + * file + */ + private static String SNYK_POLICY_FILE_NAME = ".snyk"; + + /** + * A location of a Snyk configuration file in a repository. + * + * @see To setup Snyk actions + * + */ + private static final String [] SNYK_CONFIGS = { + ".github/workflows/snyk.yaml", + ".github/workflows/snyk.yml" + }; + + /** + * Predicate to confirm if there is a file in open-source project with the .snyk extension. + */ + private static final Predicate SNYK_FILE_PREDICATE = + path -> path.getFileName().toString().endsWith(SNYK_POLICY_FILE_NAME); + + /** + * A pattern to detect commits by Snyk. + * + * @see Snyk + * commit signing + */ + private static final String SNYK_PATTERN = "snyk"; + + @Override + protected String getDependencyCheckerPattern() { + return SNYK_PATTERN; + } + + /** + * Initializes a data provider. + * + * @param fetcher An interface to GitHub. + */ + public UsesSnyk(GitHubDataFetcher fetcher) { + super(fetcher); + } + + @Override + public Set> supportedFeatures() { + return setOf(USES_SNYK, HAS_OPEN_PULL_REQUEST_FROM_SNYK); + } + + @Override + protected ValueSet fetchValuesFor(GitHubProject project) throws IOException { + logger.info("Checking how the project uses Snyk ..."); + + LocalRepository repository = GitHubDataFetcher.localRepositoryFor(project); + + return ValueHashSet.from( + USES_SNYK.value( + hasSnykPolicy(repository) + || hasDependencyCheckerConfig(repository, SNYK_CONFIGS) + || hasDependencyCheckerCommits(repository)), + HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(hasOpenPullRequestFromDependencyChecker(project))); + } + + /** + * Checks if a repository has a policy file for Snyk. + * + * @param repository The repository + * @return True if a policy file was found, false otherwise. + */ + private boolean hasSnykPolicy(LocalRepository repository) throws IOException { + List snykPolicyFilePaths = repository.files(SNYK_FILE_PREDICATE); + return !snykPolicyFilePaths.isEmpty(); + } +} diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/Score.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/Score.java index 760b54b43..354eeefed 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/Score.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/Score.java @@ -137,4 +137,14 @@ static double adjust(double value) { return value; } + /** + * Get an Interval with the provided range. + * + * @param min An interval start value. + * @param max An interval end value. + * @return Interval with the range provided from min and max param values. + */ + static Interval makeInterval(double min, double max) { + return DoubleInterval.init().from(min).to(max).closed().make(); + } } diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/feature/oss/OssFeatures.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/feature/oss/OssFeatures.java index c9da290fc..b3884d6af 100755 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/feature/oss/OssFeatures.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/feature/oss/OssFeatures.java @@ -171,6 +171,29 @@ private OssFeatures() { public static final BooleanFeature HAS_OPEN_PULL_REQUEST_FROM_DEPENDABOT = new BooleanFeature("If a project has open pull requests from Dependabot"); + /** + *

Shows if a project uses Snyk.

+ *

Snyk introduction offers

+ *
    + *
  • Static Application Security Testing (SAST)
  • + *
  • Automatic dependency updates
  • + *
+ *

In particular for automatic dependency updates, + * when Snyk finds a vulnerability in dependencies, + * it opens a pull request to update the vulnerable dependency to the safe version.

+ */ + public static final Feature USES_SNYK + = new BooleanFeature("If a project uses Snyk"); + + /** + * Shows if an open source project has open pull requests from Snyk which means that + * there are dependencies with known vulnerabilities. + * + * @see Snyk + */ + public static final BooleanFeature HAS_OPEN_PULL_REQUEST_FROM_SNYK + = new BooleanFeature("If a project has open pull requests from Snyk"); + /** * Shows how many GitHub users starred an open-source project. */ diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScore.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScore.java index 49d835fb4..9d9a46691 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScore.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScore.java @@ -70,7 +70,7 @@ public class DependabotScore extends FeatureBasedScore { * A score value that is returned if it's likely * that a project uses the security alerts on GitHub. */ - private static final double GITHUB_ALERTS_SCORE_VALUE = 6.0; + private static final double GITHUB_ALERTS_SCORE_VALUE = 5.0; /** * Initializes a new score. diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScore.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScore.java index 77454fdc8..9e23ffd29 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScore.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScore.java @@ -18,6 +18,7 @@ *
    *
  • {@link DependabotScore}
  • *
  • {@link OwaspDependencyScanScore}
  • + *
  • {@link SnykDependencyScanScore}
  • *
*/ public class DependencyScanScore extends AbstractScore { @@ -32,11 +33,17 @@ public class DependencyScanScore extends AbstractScore { */ private final OwaspDependencyScanScore owaspDependencyCheckScore; + /** + * A score that shows how a project uses Snyk. + */ + private final SnykDependencyScanScore snykDependencyScanScore; + /** * Initializes a new score. */ public DependencyScanScore() { super("How a project scans its dependencies for vulnerabilities"); + this.snykDependencyScanScore = new SnykDependencyScanScore(); this.dependabotScore = new DependabotScore(); this.owaspDependencyCheckScore = new OwaspDependencyScanScore(); } @@ -48,30 +55,36 @@ public Set> features() { @Override public Set subScores() { - return setOf(dependabotScore, owaspDependencyCheckScore); + return setOf(dependabotScore, snykDependencyScanScore, owaspDependencyCheckScore); } @Override public ScoreValue calculate(Value... values) { Objects.requireNonNull(values, "Oh no! Values is null!"); + ScoreValue snykDependencyScanScoreValue = calculateIfNecessary(snykDependencyScanScore, values); ScoreValue dependabotScoreValue = calculateIfNecessary(dependabotScore, values); ScoreValue owaspDependencyCheckScoreValue = calculateIfNecessary(owaspDependencyCheckScore, values); - ScoreValue scoreValue = scoreValue(MIN, dependabotScoreValue, owaspDependencyCheckScoreValue); + ScoreValue scoreValue = scoreValue(MIN, dependabotScoreValue, + snykDependencyScanScoreValue, owaspDependencyCheckScoreValue); - if (allUnknown(dependabotScoreValue, owaspDependencyCheckScoreValue)) { + if (allUnknown(dependabotScoreValue, snykDependencyScanScoreValue, + owaspDependencyCheckScoreValue)) { return scoreValue.makeUnknown(); } - if (allNotApplicable(dependabotScoreValue, owaspDependencyCheckScoreValue)) { + if (allNotApplicable(dependabotScoreValue, snykDependencyScanScoreValue, + owaspDependencyCheckScoreValue)) { return scoreValue.makeNotApplicable(); } + scoreValue.increase(snykDependencyScanScoreValue.orElse(MIN)); scoreValue.increase(dependabotScoreValue.orElse(MIN)); scoreValue.increase(owaspDependencyCheckScoreValue.orElse(MIN)); - scoreValue.confidence(Confidence.make(dependabotScoreValue, owaspDependencyCheckScoreValue)); + scoreValue.confidence(Confidence.make(dependabotScoreValue, snykDependencyScanScoreValue, + owaspDependencyCheckScoreValue)); return scoreValue; } diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScore.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScore.java index 8830a1c09..e6e16ebd3 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScore.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScore.java @@ -17,6 +17,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_ENCODER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.value.OwaspDependencyCheckUsage.NOT_USED; @@ -62,6 +63,9 @@ * {@link com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures#USES_DEPENDABOT} * *
  • + * {@link com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures#USES_SNYK} + *
  • + *
  • * {@link com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures#USES_NOHTTP} *
  • *
  • @@ -157,7 +161,7 @@ public class ProjectSecurityAwarenessScore extends FeatureBasedScore { */ private static final Feature[] SECURITY_TOOLS_FEATURES = new Feature[] { FUZZED_IN_OSS_FUZZ, - USES_DEPENDABOT, + USES_DEPENDABOT, USES_SNYK, USES_NOHTTP, USES_LGTM_CHECKS, USES_FIND_SEC_BUGS, USES_OWASP_ESAPI, USES_OWASP_JAVA_ENCODER, USES_OWASP_JAVA_HTML_SANITIZER, USES_ADDRESS_SANITIZER, USES_MEMORY_SANITIZER, USES_UNDEFINED_BEHAVIOR_SANITIZER, diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScore.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScore.java new file mode 100644 index 000000000..aad055652 --- /dev/null +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScore.java @@ -0,0 +1,114 @@ +package com.sap.oss.phosphor.fosstars.model.score.oss; + +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.LANGUAGES; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_GITHUB_FOR_DEVELOPMENT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; +import static com.sap.oss.phosphor.fosstars.model.value.Language.CPP; +import static com.sap.oss.phosphor.fosstars.model.value.Language.C_SHARP; +import static com.sap.oss.phosphor.fosstars.model.value.Language.F_SHARP; +import static com.sap.oss.phosphor.fosstars.model.value.Language.GO; +import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVA; +import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVASCRIPT; +import static com.sap.oss.phosphor.fosstars.model.value.Language.PHP; +import static com.sap.oss.phosphor.fosstars.model.value.Language.PYTHON; +import static com.sap.oss.phosphor.fosstars.model.value.Language.RUBY; +import static com.sap.oss.phosphor.fosstars.model.value.Language.SCALA; +import static com.sap.oss.phosphor.fosstars.model.value.Language.VISUALBASIC; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.COMPOSER; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.DOTNET; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GOMODULES; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.MAVEN; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.NPM; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.PIP; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.RUBYGEMS; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.YARN; + +import com.sap.oss.phosphor.fosstars.model.Score; +import com.sap.oss.phosphor.fosstars.model.Value; +import com.sap.oss.phosphor.fosstars.model.score.FeatureBasedScore; +import com.sap.oss.phosphor.fosstars.model.value.Languages; +import com.sap.oss.phosphor.fosstars.model.value.PackageManager; +import com.sap.oss.phosphor.fosstars.model.value.PackageManagers; +import com.sap.oss.phosphor.fosstars.model.value.ScoreValue; +import java.util.HashMap; +import java.util.Map; + +public class SnykDependencyScanScore extends FeatureBasedScore { + + private static final Map SUPPORTED_LANGUAGES = new HashMap<>(); + + static { + SUPPORTED_LANGUAGES.put(MAVEN, Languages.of(JAVA, SCALA)); + SUPPORTED_LANGUAGES.put(NPM, Languages.of(JAVASCRIPT)); + SUPPORTED_LANGUAGES.put(YARN, Languages.of(JAVASCRIPT)); + SUPPORTED_LANGUAGES.put(DOTNET, Languages.of(C_SHARP, F_SHARP, CPP, VISUALBASIC)); + SUPPORTED_LANGUAGES.put(PIP, Languages.of(PYTHON)); + SUPPORTED_LANGUAGES.put(RUBYGEMS, Languages.of(RUBY)); + SUPPORTED_LANGUAGES.put(COMPOSER, Languages.of(PHP)); + SUPPORTED_LANGUAGES.put(GOMODULES, Languages.of(GO)); + } + + /** + * A score value that is returned if it's likely that a project uses the security alerts on + * GitHub. + */ + private static final double GITHUB_ALERTS_SCORE_VALUE = 5.0; + + /** Initializes a new score. */ + public SnykDependencyScanScore() { + super( + "How a project uses Snyk", + USES_SNYK, + USES_GITHUB_FOR_DEVELOPMENT, + PACKAGE_MANAGERS, + LANGUAGES); + } + + @Override + public ScoreValue calculate(Value... values) { + Value usesSnyk = find(USES_SNYK, values); + Value usesGithub = find(USES_GITHUB_FOR_DEVELOPMENT, values); + Value languages = find(LANGUAGES, values); + Value packageManagers = find(PACKAGE_MANAGERS, values); + + ScoreValue scoreValue = scoreValue(Score.MIN, usesSnyk, usesGithub, packageManagers, languages); + + if (allUnknown(scoreValue.usedValues())) { + return scoreValue.makeUnknown(); + } + + // if the project uses Snyk, + // then we're happy + if (usesSnyk.orElse(false)) { + return scoreValue.set(Score.MAX); + } + + // if the projects uses GitHub for development, + // then there is a chance that it takes advantage of security alerts, + // although we can't tell for sure + if (usesGithub.orElse(false)) { + boolean snykCanBeUsed = + packageManagers.orElse(PackageManagers.empty()).list().stream() + .anyMatch(SUPPORTED_LANGUAGES::containsKey); + + // if the project does not use any package ecosystem that is supported by Snyk, + // then a score is not applicable + if (!snykCanBeUsed) { + return scoreValue.makeNotApplicable(); + } + + Languages usedLanguages = languages.orElse(Languages.empty()); + for (PackageManager packageManager : packageManagers.orElse(PackageManagers.empty())) { + Languages supportedLanguages = + SUPPORTED_LANGUAGES.getOrDefault(packageManager, Languages.empty()); + if (supportedLanguages.containsAnyOf(usedLanguages)) { + return scoreValue.set(GITHUB_ALERTS_SCORE_VALUE); + } + } + } + + // otherwise, return the minimal score + return scoreValue.set(Score.MIN); + } +} diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/model/value/PackageManager.java b/src/main/java/com/sap/oss/phosphor/fosstars/model/value/PackageManager.java index 4b84c12df..4a6d05826 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/model/value/PackageManager.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/model/value/PackageManager.java @@ -7,7 +7,7 @@ public enum PackageManager { MAVEN, GRADLE, - NPM, YARN, DOTNET, PIP, RUBYGEMS, COMPOSER, + NPM, YARN, DOTNET, PIP, RUBYGEMS, COMPOSER, GOMODULES, OTHER } diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/tool/format/CommonFormatter.java b/src/main/java/com/sap/oss/phosphor/fosstars/tool/format/CommonFormatter.java index a70eb625b..6f03d459f 100755 --- a/src/main/java/com/sap/oss/phosphor/fosstars/tool/format/CommonFormatter.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/tool/format/CommonFormatter.java @@ -13,6 +13,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_EXECUTABLE_BINARIES; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_LICENSE; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_OPEN_PULL_REQUEST_FROM_DEPENDABOT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_OPEN_PULL_REQUEST_FROM_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_README; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_REQUIRED_TEXT_IN_CODE_OF_CONDUCT_GUIDELINE; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_REQUIRED_TEXT_IN_CONTRIBUTING_GUIDELINE; @@ -50,6 +51,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_REUSE; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_ARTIFACT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_PROJECT; @@ -82,6 +84,7 @@ import com.sap.oss.phosphor.fosstars.model.score.oss.ProjectSecurityAwarenessScore; import com.sap.oss.phosphor.fosstars.model.score.oss.ProjectSecurityTestingScore; import com.sap.oss.phosphor.fosstars.model.score.oss.SecurityReviewScore; +import com.sap.oss.phosphor.fosstars.model.score.oss.SnykDependencyScanScore; import com.sap.oss.phosphor.fosstars.model.score.oss.StaticAnalysisScore; import com.sap.oss.phosphor.fosstars.model.score.oss.UnpatchedVulnerabilitiesScore; import com.sap.oss.phosphor.fosstars.model.score.oss.VulnerabilityDiscoveryAndSecurityTestingScore; @@ -123,6 +126,7 @@ public abstract class CommonFormatter implements Formatter { add(VulnerabilityLifetimeScore.class, "Vulnerability lifetime"); add(MemorySafetyTestingScore.class, "Memory-safety testing"); add(DependabotScore.class, "Dependabot score"); + add(SnykDependencyScanScore.class, "Snyk score"); add(OwaspDependencyScanScore.class, "OWASP Dependency Check score"); add(DependencyScanScore.class, "Dependency testing"); add(FuzzingScore.class, "Fuzzing"); @@ -175,6 +179,7 @@ private static void add(Class> clazz, String caption) { add(USES_OWASP_JAVA_ENCODER, "Does it use OWASP Java Encoder?"); add(USES_OWASP_JAVA_HTML_SANITIZER, "Does it use OWASP Java HTML Sanitizer?"); add(USES_DEPENDABOT, "Does it use Dependabot?"); + add(USES_SNYK, "Does it use Snyk?"); add(USES_LGTM_CHECKS, "Does it use LGTM checks?"); add(HAS_BUG_BOUNTY_PROGRAM, "Does it have a bug bounty program?"); add(SIGNS_ARTIFACTS, "Does it sign artifacts?"); @@ -190,6 +195,8 @@ private static void add(Class> clazz, String caption) { add(IS_REUSE_COMPLIANT, "Is it compliant with REUSE rules?"); add(HAS_OPEN_PULL_REQUEST_FROM_DEPENDABOT, "Does the project have open pull requests from Dependabot?"); + add(HAS_OPEN_PULL_REQUEST_FROM_SNYK, + "Does the project have open pull requests from Snyk?"); add(PACKAGE_MANAGERS, "Package managers"); add(LANGUAGES, "Programming languages"); add(RUNS_CODEQL_SCANS, "Does it run CodeQL scans?"); diff --git a/src/main/java/com/sap/oss/phosphor/fosstars/util/Deserialization.java b/src/main/java/com/sap/oss/phosphor/fosstars/util/Deserialization.java index 1ee7a8d82..a1cca5760 100644 --- a/src/main/java/com/sap/oss/phosphor/fosstars/util/Deserialization.java +++ b/src/main/java/com/sap/oss/phosphor/fosstars/util/Deserialization.java @@ -68,6 +68,7 @@ import com.sap.oss.phosphor.fosstars.model.score.oss.ProjectSecurityAwarenessScore; import com.sap.oss.phosphor.fosstars.model.score.oss.ProjectSecurityTestingScore; import com.sap.oss.phosphor.fosstars.model.score.oss.SecurityReviewScore; +import com.sap.oss.phosphor.fosstars.model.score.oss.SnykDependencyScanScore; import com.sap.oss.phosphor.fosstars.model.score.oss.StaticAnalysisScore; import com.sap.oss.phosphor.fosstars.model.score.oss.UnpatchedVulnerabilitiesScore; import com.sap.oss.phosphor.fosstars.model.score.oss.VulnerabilityDiscoveryAndSecurityTestingScore; @@ -289,6 +290,7 @@ static ObjectMapper registerSubTypesIn(ObjectMapper mapper) { FuzzingScore.class, StaticAnalysisScore.class, DependabotScore.class, + SnykDependencyScanScore.class, OwaspDependencyScanScore.class, VulnerabilityDiscoveryAndSecurityTestingScore.class, SecurityReviewScore.class, diff --git a/src/main/resources/com/sap/oss/phosphor/fosstars/advice/oss/OssAdvice.yml b/src/main/resources/com/sap/oss/phosphor/fosstars/advice/oss/OssAdvice.yml index 5ebaca9d7..c5ad552c2 100644 --- a/src/main/resources/com/sap/oss/phosphor/fosstars/advice/oss/OssAdvice.yml +++ b/src/main/resources/com/sap/oss/phosphor/fosstars/advice/oss/OssAdvice.yml @@ -89,6 +89,12 @@ If a project uses Dependabot: links: - name: Configuration options for dependency updates url: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates +If a project uses Snyk: + - advice: > + You can create Snyk account and configure your project. + links: + - name: Getting started with snyk for open source + url: https://docs.snyk.io/products/snyk-open-source/getting-started-snyk-open-source How OWASP Dependency Check is used: - advice: > You can add OWASP Dependency Check to the project's build pipeline. diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/TestUtils.java b/src/test/java/com/sap/oss/phosphor/fosstars/TestUtils.java index 72a5c192e..018c927e4 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/TestUtils.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/TestUtils.java @@ -37,6 +37,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_ENCODER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_ARTIFACT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_PROJECT; @@ -200,6 +201,7 @@ public static Set> getDefaultValues() { WORST_LGTM_GRADE.value(LgtmGrade.B), USES_NOHTTP.value(true), USES_DEPENDABOT.value(true), + USES_SNYK.value(false), USES_GITHUB_FOR_DEVELOPMENT.value(true), LANGUAGES.value(Languages.of(JAVA)), USES_ADDRESS_SANITIZER.value(false), @@ -275,6 +277,7 @@ public static Set> getBestValues() { WORST_LGTM_GRADE.value(LgtmGrade.A_PLUS), USES_NOHTTP.value(true), USES_DEPENDABOT.value(true), + USES_SNYK.value(true), USES_GITHUB_FOR_DEVELOPMENT.value(true), LANGUAGES.value(Languages.of(JAVA)), USES_ADDRESS_SANITIZER.value(true), diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/BanditAdvisorTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/BanditAdvisorTest.java index 3b9d4b7e2..8f606ea02 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/BanditAdvisorTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/BanditAdvisorTest.java @@ -2,10 +2,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.LANGUAGES; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.RUNS_BANDIT_SCANS; -import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.RUNS_CODEQL_SCANS; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_BANDIT_SCAN_CHECKS; -import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_CODEQL_CHECKS; -import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_LGTM_CHECKS; import static com.sap.oss.phosphor.fosstars.model.other.Utils.allUnknown; import static com.sap.oss.phosphor.fosstars.model.value.Language.PYTHON; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisorTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisorTest.java new file mode 100644 index 000000000..0c7207d2e --- /dev/null +++ b/src/test/java/com/sap/oss/phosphor/fosstars/advice/oss/SnykAdvisorTest.java @@ -0,0 +1,73 @@ +package com.sap.oss.phosphor.fosstars.advice.oss; + +import static com.sap.oss.phosphor.fosstars.advice.oss.AbstractOssAdvisor.OssAdviceContextFactory.WITH_EMPTY_CONTEXT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.LANGUAGES; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_GITHUB_FOR_DEVELOPMENT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; +import static com.sap.oss.phosphor.fosstars.model.other.Utils.allUnknown; +import static com.sap.oss.phosphor.fosstars.model.value.Language.C; +import static com.sap.oss.phosphor.fosstars.model.value.Language.GO; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GOMODULES; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.OTHER; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.sap.oss.phosphor.fosstars.model.Rating; +import com.sap.oss.phosphor.fosstars.model.RatingRepository; +import com.sap.oss.phosphor.fosstars.model.ValueSet; +import com.sap.oss.phosphor.fosstars.model.rating.oss.OssSecurityRating; +import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject; +import com.sap.oss.phosphor.fosstars.model.value.Languages; +import com.sap.oss.phosphor.fosstars.model.value.PackageManagers; +import com.sap.oss.phosphor.fosstars.model.value.ValueHashSet; +import java.net.MalformedURLException; +import org.junit.Test; + +public class SnykAdvisorTest { + + @Test + public void testAdviseForSnyk() throws MalformedURLException { + final SnykAdvisor advisor = new SnykAdvisor(WITH_EMPTY_CONTEXT); + GitHubProject project = new GitHubProject("org", "test"); + + // no advice if no rating value is set + assertTrue(advisor.adviceFor(project).isEmpty()); + + Rating rating = RatingRepository.INSTANCE.rating(OssSecurityRating.class); + ValueSet values = new ValueHashSet(); + + // no advice for an unknown values + values.update(allUnknown(rating.score().allFeatures())); + values.update(LANGUAGES.value(Languages.of(GO))); + values.update(PACKAGE_MANAGERS.value(PackageManagers.from(GOMODULES))); + values.update(USES_GITHUB_FOR_DEVELOPMENT.value(true)); + assertTrue(advisor.adviceFor(project).isEmpty()); + + values.update(USES_SNYK.value(true)); + project.set(rating.calculate(values)); + assertTrue(advisor.adviceFor(project).isEmpty()); + + values.update(USES_SNYK.value(false)); + project.set(rating.calculate(values)); + assertEquals(1, advisor.adviceFor(project).size()); + assertEquals("You can create Snyk account and configure your project.", + advisor.adviceFor(project).get(0).content().text()); + } + + @Test + public void testAdviceWhenSnykScoreIsNotApplicable() throws MalformedURLException { + final SnykAdvisor advisor = new SnykAdvisor(WITH_EMPTY_CONTEXT); + final GitHubProject project = new GitHubProject("org", "test"); + + Rating rating = RatingRepository.INSTANCE.rating(OssSecurityRating.class); + ValueSet values = new ValueHashSet(); + values.update(allUnknown(rating.score().allFeatures())); + values.update(USES_SNYK.value(false)); + values.update(USES_GITHUB_FOR_DEVELOPMENT.value(true)); + values.update(LANGUAGES.value(Languages.of(C))); + values.update(PACKAGE_MANAGERS.value(PackageManagers.from(OTHER))); + project.set(rating.calculate(values)); + assertTrue(advisor.adviceFor(project).isEmpty()); + } +} diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/data/github/CodeOfConductGuidelineInfoTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/data/github/CodeOfConductGuidelineInfoTest.java index 11f85788f..2f831832a 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/data/github/CodeOfConductGuidelineInfoTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/data/github/CodeOfConductGuidelineInfoTest.java @@ -13,8 +13,6 @@ import com.sap.oss.phosphor.fosstars.model.ValueSet; import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject; import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnykTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnykTest.java new file mode 100644 index 000000000..2058f15c1 --- /dev/null +++ b/src/test/java/com/sap/oss/phosphor/fosstars/data/github/UsesSnykTest.java @@ -0,0 +1,202 @@ +package com.sap.oss.phosphor.fosstars.data.github; + +import static com.sap.oss.phosphor.fosstars.data.github.TestGitHubDataFetcherHolder.TestGitHubDataFetcher.addForTesting; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.HAS_OPEN_PULL_REQUEST_FROM_SNYK; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.sap.oss.phosphor.fosstars.data.SubjectValueCache; +import com.sap.oss.phosphor.fosstars.model.Feature; +import com.sap.oss.phosphor.fosstars.model.Value; +import com.sap.oss.phosphor.fosstars.model.ValueSet; +import com.sap.oss.phosphor.fosstars.model.subject.oss.GitHubProject; +import com.sap.oss.phosphor.fosstars.model.value.ValueHashSet; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import org.junit.Test; +import org.kohsuke.github.GHPullRequest; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHUser; + +public class UsesSnykTest extends TestGitHubDataFetcherHolder { + + private static final GitHubProject PROJECT = new GitHubProject("org", "test"); + + @Test + public void testWithSnykInAuthorName() throws IOException { + final List commits = new ArrayList<>(); + + Commit commit = mock(Commit.class); + when(commit.date()).thenReturn(new Date()); + when(commit.authorName()).thenReturn("snyk"); + when(commit.committerName()).thenReturn("GitHub"); + commits.add(commit); + + Commit otherCommit = mock(Commit.class); + when(otherCommit.date()).thenReturn(new Date()); + when(otherCommit.authorName()).thenReturn("Mr. Test"); + when(otherCommit.committerName()).thenReturn("Mr. Test"); + commits.add(otherCommit); + + LocalRepository repository = mock(LocalRepository.class); + when(repository.commitsAfter(any())).thenReturn(commits); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + + testProvider(USES_SNYK.value(true), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(false)); + } + + @Test + public void testWithSnykInCommitterName() throws IOException { + final List commits = new ArrayList<>(); + + for (int i = 0; i < 1000; i++) { + Commit otherCommit = mock(Commit.class); + when(otherCommit.date()).thenReturn(new Date()); + when(otherCommit.authorName()).thenReturn("Someone"); + when(otherCommit.committerName()).thenReturn("Someone"); + commits.add(otherCommit); + } + + Commit commit = mock(Commit.class); + when(commit.date()).thenReturn(new Date()); + when(commit.authorName()).thenReturn("GitHub"); + when(commit.committerName()).thenReturn("snyk"); + commits.add(commit); + + LocalRepository repository = mock(LocalRepository.class); + when(repository.commitsAfter(any())).thenReturn(commits); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + + testProvider(USES_SNYK.value(true), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(false)); + } + + @Test + public void testWithSnykInCommitMessage() throws IOException { + final List commits = new ArrayList<>(); + + Commit commit = mock(Commit.class); + when(commit.date()).thenReturn(new Date()); + when(commit.authorName()).thenReturn("GitHub"); + when(commit.committerName()).thenReturn("GitHub"); + when(commit.message()).thenReturn(Arrays.asList("Commit message", "Signed-off-by: snyk")); + commits.add(commit); + + Commit otherCommit = mock(Commit.class); + when(otherCommit.date()).thenReturn(new Date()); + when(otherCommit.authorName()).thenReturn("Mr. Pink"); + when(otherCommit.committerName()).thenReturn("Mr. Pink"); + commits.add(otherCommit); + + LocalRepository repository = mock(LocalRepository.class); + when(repository.commitsAfter(any())).thenReturn(commits); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + + testProvider(USES_SNYK.value(true), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(false)); + } + + @Test + public void testWithSnykPolicyFile() throws IOException { + Path tempDirectory = Files.createTempDirectory(getClass().getName()); + File snykPolicyFile = tempDirectory.resolve(".snyk").toFile(); + List snykFilePaths = new ArrayList<>(); + snykFilePaths.add(snykPolicyFile.toPath()); + final LocalRepository repository = mock(LocalRepository.class); + when(repository.files(any())).thenReturn(snykFilePaths); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + + testProvider(USES_SNYK.value(true), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(false)); + } + + @Test + public void testNoSnyk() throws IOException { + final List commits = new ArrayList<>(); + + Commit commit = mock(Commit.class); + when(commit.date()).thenReturn(new Date()); + when(commit.authorName()).thenReturn("Mr. Orange"); + when(commit.committerName()).thenReturn("Mr. Orange"); + when(commit.message()).thenReturn(Arrays.asList("Commit message", "Signed-off-by: Mr. Pink")); + commits.add(commit); + + commit = mock(Commit.class); + when(commit.date()).thenReturn(new Date()); + when(commit.authorName()).thenReturn("Mr. Pink"); + when(commit.committerName()).thenReturn("Mr. Pink"); + when(commit.message()).thenReturn(Arrays.asList("Commit message", "Signed-off-by: Mr. Orange")); + commits.add(commit); + + LocalRepository repository = mock(LocalRepository.class); + when(repository.files(any())).thenReturn(new ArrayList<>()); + when(repository.commitsAfter(any())).thenReturn(commits); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + + testProvider(USES_SNYK.value(false), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(false)); + } + + @Test + public void testWithOpenPullRequestFromSnyk() throws IOException { + Path tempDirectory = Files.createTempDirectory(getClass().getName()); + File snykPolicyFile = tempDirectory.resolve(".snyk").toFile(); + List snykFilePaths = new ArrayList<>(); + snykFilePaths.add(snykPolicyFile.toPath()); + final LocalRepository repository = mock(LocalRepository.class); + when(repository.files(any())).thenReturn(snykFilePaths); + addForTesting(PROJECT, repository); + + GHRepository githubRepository = mock(GHRepository.class); + when(fetcher.github().getRepository(any())).thenReturn(githubRepository); + GHPullRequest pullRequest = mock(GHPullRequest.class); + GHUser user = mock(GHUser.class); + when(user.getName()).thenReturn("snyk"); + when(pullRequest.getUser()).thenReturn(user); + when(githubRepository.getPullRequests(any())) + .thenReturn(Collections.singletonList(pullRequest)); + + testProvider(USES_SNYK.value(true), HAS_OPEN_PULL_REQUEST_FROM_SNYK.value(true)); + } + + private void testProvider(Value... expectedValues) throws IOException { + UsesSnyk provider = new UsesSnyk(fetcher); + provider.set(new SubjectValueCache()); + + ValueSet values = new ValueHashSet(); + assertEquals(0, values.size()); + + provider.update(PROJECT, values); + assertEquals(expectedValues.length, values.size()); + for (Value expectedValue : expectedValues) { + Feature feature = expectedValue.feature(); + assertTrue(values.has(feature)); + Optional> actualValue = values.of(feature); + assertTrue(actualValue.isPresent()); + assertEquals(expectedValue, actualValue.get()); + } + } +} diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTest.java index 71d7aabc6..bb488565a 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTest.java @@ -7,6 +7,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_DEPENDABOT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_GITHUB_FOR_DEVELOPMENT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.other.Utils.setOf; import static com.sap.oss.phosphor.fosstars.model.value.Language.JAVA; import static com.sap.oss.phosphor.fosstars.model.value.OwaspDependencyCheckUsage.MANDATORY; @@ -33,12 +34,13 @@ public void testCalculate() { OWASP_DEPENDENCY_CHECK_FAIL_CVSS_THRESHOLD.value(7.0), USES_GITHUB_FOR_DEVELOPMENT.value(true), USES_DEPENDABOT.value(true), + USES_SNYK.value(false), LANGUAGES.value(Languages.of(JAVA)), PACKAGE_MANAGERS.value(PackageManagers.from(MAVEN)) )); assertTrue(Score.INTERVAL.contains(scoreValue.get())); - assertEquals(2, scoreValue.usedValues().size()); + assertEquals(3, scoreValue.usedValues().size()); } @Test diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/OssSecurityScoreTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/OssSecurityScoreTest.java index 9c39c11f7..ec41c3f04 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/OssSecurityScoreTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/OssSecurityScoreTest.java @@ -36,6 +36,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_ENCODER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_PROJECT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.WORST_LGTM_GRADE; @@ -119,6 +120,7 @@ public static Set> defaultValues() { WORST_LGTM_GRADE.value(LgtmGrade.B), USES_NOHTTP.value(true), USES_DEPENDABOT.value(true), + USES_SNYK.value(false), USES_GITHUB_FOR_DEVELOPMENT.value(true), LANGUAGES.value(Languages.of(JAVA)), USES_ADDRESS_SANITIZER.value(false), diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTest.java new file mode 100644 index 000000000..c8e24ef61 --- /dev/null +++ b/src/test/java/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTest.java @@ -0,0 +1,64 @@ +package com.sap.oss.phosphor.fosstars.model.score.oss; + +import static com.sap.oss.phosphor.fosstars.TestUtils.DELTA; +import static com.sap.oss.phosphor.fosstars.TestUtils.assertScore; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.LANGUAGES; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.PACKAGE_MANAGERS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_GITHUB_FOR_DEVELOPMENT; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; +import static com.sap.oss.phosphor.fosstars.model.other.Utils.setOf; +import static com.sap.oss.phosphor.fosstars.model.value.Language.GO; +import static com.sap.oss.phosphor.fosstars.model.value.PackageManager.GOMODULES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.sap.oss.phosphor.fosstars.model.Confidence; +import com.sap.oss.phosphor.fosstars.model.Score; +import com.sap.oss.phosphor.fosstars.model.other.Utils; +import com.sap.oss.phosphor.fosstars.model.value.Languages; +import com.sap.oss.phosphor.fosstars.model.value.PackageManagers; +import com.sap.oss.phosphor.fosstars.model.value.ScoreValue; +import org.junit.Test; + +public class SnykDependencyScanScoreTest { + + private static final SnykDependencyScanScore SCORE = new SnykDependencyScanScore(); + + @Test + public void testCalculateWhenSnykIsUsed() { + assertScore( + Score.makeInterval(9, 10), + SCORE, + setOf( + USES_GITHUB_FOR_DEVELOPMENT.value(true), + USES_SNYK.value(true), + LANGUAGES.value(Languages.of(GO)), + PACKAGE_MANAGERS.value(PackageManagers.from(GOMODULES)))); + } + + @Test + public void testCalculateWhenSnykIsNotUsed() { + assertScore( + Score.makeInterval(0, 5), + SCORE, + setOf( + USES_GITHUB_FOR_DEVELOPMENT.value(true), + USES_SNYK.value(false), + LANGUAGES.value(Languages.of(GO)), + PACKAGE_MANAGERS.value(PackageManagers.from(GOMODULES)))); + } + + @Test + public void testCalculateWithAllUnknown() { + ScoreValue scoreValue = SCORE.calculate(Utils.allUnknown(SCORE.allFeatures())); + assertTrue(scoreValue.isUnknown()); + assertEquals(Confidence.MIN, scoreValue.confidence(), DELTA); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithNoInfo() { + new DependencyScanScore().calculate(); + } +} + + diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/OssSecurityRatingMarkdownFormatterTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/OssSecurityRatingMarkdownFormatterTest.java index 2a1824544..02bb8e75e 100755 --- a/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/OssSecurityRatingMarkdownFormatterTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/OssSecurityRatingMarkdownFormatterTest.java @@ -36,6 +36,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_ENCODER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_PROJECT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.WORST_LGTM_GRADE; @@ -97,6 +98,7 @@ public class OssSecurityRatingMarkdownFormatterTest { USES_GITHUB_FOR_DEVELOPMENT.value(false), USES_NOHTTP.value(false), USES_DEPENDABOT.value(false), + USES_SNYK.value(false), USES_ADDRESS_SANITIZER.value(false), USES_MEMORY_SANITIZER.value(false), USES_UNDEFINED_BEHAVIOR_SANITIZER.value(false), diff --git a/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/PrettyPrinterTest.java b/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/PrettyPrinterTest.java index 5925f344a..5c755bbde 100644 --- a/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/PrettyPrinterTest.java +++ b/src/test/java/com/sap/oss/phosphor/fosstars/tool/format/PrettyPrinterTest.java @@ -36,6 +36,7 @@ import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_ENCODER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_OWASP_JAVA_HTML_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SIGNED_COMMITS; +import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_SNYK; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.USES_UNDEFINED_BEHAVIOR_SANITIZER; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.VULNERABILITIES_IN_PROJECT; import static com.sap.oss.phosphor.fosstars.model.feature.oss.OssFeatures.WORST_LGTM_GRADE; @@ -99,6 +100,7 @@ public class PrettyPrinterTest { USES_GITHUB_FOR_DEVELOPMENT.value(false), USES_NOHTTP.value(false), USES_DEPENDABOT.value(false), + USES_SNYK.value(false), USES_ADDRESS_SANITIZER.value(false), USES_MEMORY_SANITIZER.value(false), USES_UNDEFINED_BEHAVIOR_SANITIZER.value(false), diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssArtifactSecurityRatingTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssArtifactSecurityRatingTestVectors.yml index 90333949a..700f0d917 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssArtifactSecurityRatingTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssArtifactSecurityRatingTestVectors.yml @@ -36,6 +36,11 @@ defaults: type: "BooleanFeature" name: "If a project uses Dependabot" flag: true + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false - type: "BooleanValue" feature: type: "BooleanFeature" @@ -286,6 +291,10 @@ elements: feature: type: "BooleanFeature" name: "If a project uses Dependabot" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" - type: "UnknownValue" feature: type: "BooleanFeature" diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssSecurityRatingTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssSecurityRatingTestVectors.yml index 04af0a0d1..4b2c63cd9 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssSecurityRatingTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/rating/oss/OssSecurityRatingTestVectors.yml @@ -7,6 +7,11 @@ defaults: type: "BooleanFeature" name: "If a project uses Dependabot" flag: false +- type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false - type: "BooleanValue" feature: type: "BooleanFeature" @@ -131,6 +136,10 @@ elements: feature: type: "BooleanFeature" name: "If a project uses Dependabot" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" - type: "UnknownValue" feature: type: "BooleanFeature" diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScoreTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScoreTestVectors.yml index 9f8311b7a..b574d2ee6 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScoreTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependabotScoreTestVectors.yml @@ -104,10 +104,10 @@ elements: - "JAVA" expectedScore: type: "DoubleInterval" - from: 6.0 + from: 5.0 openLeft: false negativeInfinity: false - to: 9.0 + to: 8.0 openRight: false positiveInfinity: false expectedLabel: null diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTestVectors.yml index 2a8d07cb7..f32d9ff58 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/DependencyScanScoreTestVectors.yml @@ -1,7 +1,12 @@ --- # default values for test vectors -defaults: [] +defaults: +- type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false # test vectors elements: @@ -21,6 +26,10 @@ elements: feature: type: "BooleanFeature" name: "If a project uses Dependabot" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" - type: "UnknownValue" feature: type: "BooleanFeature" @@ -134,10 +143,10 @@ elements: - "JAVA" expectedScore: type: "DoubleInterval" - from: 5.0 + from: 8.0 openLeft: false negativeInfinity: false - to: 8.0 + to: 10.0 openRight: false positiveInfinity: false expectedLabel: null diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScoreTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScoreTestVectors.yml index 6d2e3c5da..37aed2815 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScoreTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/ProjectSecurityAwarenessScoreTestVectors.yml @@ -5,6 +5,11 @@ defaults: type: "BooleanFeature" name: "If a project uses Dependabot" flag: false +- type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false - type: "BooleanValue" feature: type: "BooleanFeature" diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTestVectors.yml new file mode 100644 index 000000000..5e041a657 --- /dev/null +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/SnykDependencyScanScoreTestVectors.yml @@ -0,0 +1,152 @@ +--- + +# default values for test vectors +defaults: [] + +# test vectors +elements: + +# all unknown +- type: "StandardTestVector" + values: + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses GitHub as the main development platform" + - type: "UnknownValue" + feature: + type: "PackageManagersFeature" + name: "A set of package managers" + - type: "UnknownValue" + feature: + type: "LanguagesFeature" + name: "A set of programming languages" + expectedScore: + type: "DoubleInterval" + from: 0.0 + openLeft: false + negativeInfinity: false + to: 1.0 + openRight: false + positiveInfinity: false + expectedUnknownScore: true + expectedLabel: null + alias: "all_unknown" + +# very bad project +- type: "StandardTestVector" + values: + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses GitHub as the main development platform" + flag: false + - type: "PackageManagersValue" + feature: + type: "PackageManagersFeature" + name: "A set of package managers" + packageManagers: + packageManagers: + - "OTHER" + - type: "LanguagesValue" + feature: + type: "LanguagesFeature" + name: "A set of programming languages" + languages: + elements: + - "OTHER" + expectedScore: + type: "DoubleInterval" + from: 0.0 + openLeft: false + negativeInfinity: false + to: 1.0 + openRight: false + positiveInfinity: false + expectedLabel: null + alias: "very_bad" + + # okay project +- type: "StandardTestVector" + values: + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: false + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses GitHub as the main development platform" + flag: true + - type: "PackageManagersValue" + feature: + type: "PackageManagersFeature" + name: "A set of package managers" + packageManagers: + packageManagers: + - "MAVEN" + - type: "LanguagesValue" + feature: + type: "LanguagesFeature" + name: "A set of programming languages" + languages: + elements: + - "JAVA" + expectedScore: + type: "DoubleInterval" + from: 5.0 + openLeft: false + negativeInfinity: false + to: 8.0 + openRight: false + positiveInfinity: false + expectedLabel: null + alias: "okay" + +# very good project +- type: "StandardTestVector" + values: + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" + flag: true + - type: "BooleanValue" + feature: + type: "BooleanFeature" + name: "If a project uses GitHub as the main development platform" + flag: true + - type: "PackageManagersValue" + feature: + type: "PackageManagersFeature" + name: "A set of package managers" + packageManagers: + packageManagers: + - "MAVEN" + - type: "LanguagesValue" + feature: + type: "LanguagesFeature" + name: "A set of programming languages" + languages: + elements: + - "JAVA" + expectedScore: + type: "DoubleInterval" + from: 9.0 + openLeft: false + negativeInfinity: false + to: 10.0 + openRight: false + positiveInfinity: false + expectedLabel: null + alias: "very_good" diff --git a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/VulnerabilityDiscoveryAndSecurityTestingScoreTestVectors.yml b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/VulnerabilityDiscoveryAndSecurityTestingScoreTestVectors.yml index d92541a7c..88012ddf9 100644 --- a/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/VulnerabilityDiscoveryAndSecurityTestingScoreTestVectors.yml +++ b/src/test/resources/com/sap/oss/phosphor/fosstars/model/score/oss/VulnerabilityDiscoveryAndSecurityTestingScoreTestVectors.yml @@ -1,5 +1,6 @@ --- defaults: [] + elements: - type: "StandardTestVector" values: @@ -15,6 +16,10 @@ elements: feature: type: "BooleanFeature" name: "If a project uses Dependabot" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" - type: "UnknownValue" feature: type: "BooleanFeature" @@ -105,6 +110,10 @@ elements: feature: type: "BooleanFeature" name: "If a project uses Dependabot" + - type: "UnknownValue" + feature: + type: "BooleanFeature" + name: "If a project uses Snyk" - type: "VulnerabilitiesValue" vulnerabilities: entries: diff --git a/src/test/shell/tool/github/lib.sh b/src/test/shell/tool/github/lib.sh index 7c15b2848..e77f949da 100644 --- a/src/test/shell/tool/github/lib.sh +++ b/src/test/shell/tool/github/lib.sh @@ -24,6 +24,7 @@ declare -a project_security_default_expected_strings=( 'Figuring out how the project uses LGTM' 'Figuring out if the project uses OWASP security libraries' 'Checking how the project uses Dependabot' + 'Checking how the project uses Snyk' 'Figuring out if the project uses GitHub for development' 'Figuring out if the project uses sanitizers' 'Figuring out if the project uses FindSecBugs' @@ -58,6 +59,7 @@ declare -a project_security_default_expected_strings=( 'Sub-score:....FindSecBugs score' 'Sub-score:....Dependency testing' 'Sub-score:....Dependabot score' + 'Sub-score:....Snyk score' 'Sub-score:....OWASP Dependency Check score' 'Sub-score:....Fuzzing' 'Sub-score:....Memory-safety testing'