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

feat: PHP Composer Analyzer Scans packages-dev by default #7114

Merged
merged 1 commit into from
Oct 30, 2024
Merged
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
23 changes: 23 additions & 0 deletions ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ public class Check extends Update {
* Whether or not the PHP Composer Analyzer is enabled.
*/
private Boolean composerAnalyzerEnabled;
/**
* Whether or not the PHP Composer Analyzer will skip "packages-dev".
*/
private Boolean composerAnalyzerSkipDev;
/**
* Whether or not the Perl CPAN File Analyzer is enabled.
*/
Expand Down Expand Up @@ -943,6 +947,24 @@ public Boolean isComposerAnalyzerEnabled() {
public void setComposerAnalyzerEnabled(Boolean composerAnalyzerEnabled) {
this.composerAnalyzerEnabled = composerAnalyzerEnabled;
}

/**
* Get the value of composerAnalyzerSkipDev.
*
* @return the value of composerAnalyzerSkipDev
*/
public Boolean isComposerAnalyzerSkipDev() {
return composerAnalyzerSkipDev;
}

/**
* Set the value of composerAnalyzerSkipDev.
*
* @param composerAnalyzerSkipDev new value of composerAnalyzerSkipDev
*/
public void setComposerAnalyzerSkipDev(Boolean composerAnalyzerSkipDev) {
this.composerAnalyzerSkipDev = composerAnalyzerSkipDev;
}

/**
* Get the value of cpanfileAnalyzerEnabled.
Expand Down Expand Up @@ -2183,6 +2205,7 @@ protected void populateSettings() throws BuildException {
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_PIPFILE_ENABLED, pipfileAnalyzerEnabled);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_POETRY_ENABLED, poetryAnalyzerEnabled);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, composerAnalyzerSkipDev);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_CPANFILE_ENABLED, cpanfileAnalyzerEnabled);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_SKIPDEV, nodePackageSkipDevDependencies);
Expand Down
1 change: 1 addition & 0 deletions ant/src/site/markdown/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pipAnalyzerEnabled | Sets whether the [experimental](../analyze
pipfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Pipfile Analyzer should be used. `enableExperimental` must be set to true. | true
poetryAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Poetry Analyzer should be used. `enableExperimental` must be set to true. | true
composerAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should be used. `enableExperimental` must be set to true. | true
composerAnalyzerSkipDev | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should skip "packages-dev" | false
cpanfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Perl CPAN File Analyzer should be used. `enableExperimental` must be set to true. | true
nodeAnalyzerEnabled | Sets whether the [retired](../analyzers/index.html) Node.js Analyzer should be used. | true
nodeAuditAnalyzerEnabled | Sets whether the Node Audit Analyzer should be used. This analyzer requires an internet connection. | true
Expand Down
2 changes: 2 additions & 0 deletions cli/src/main/java/org/owasp/dependencycheck/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ protected void populateSettings(CliParser cli) throws InvalidSettingException {
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_OPENSSL, Settings.KEYS.ANALYZER_OPENSSL_ENABLED));
settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED,
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_COMPOSER, Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED));
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV,
cli.hasOption(CliParser.ARGUMENT.COMPOSER_LOCK_SKIP_DEV));
settings.setBoolean(Settings.KEYS.ANALYZER_CPANFILE_ENABLED,
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_CPAN, Settings.KEYS.ANALYZER_CPANFILE_ENABLED));
settings.setBoolean(Settings.KEYS.ANALYZER_GOLANG_DEP_ENABLED,
Expand Down
5 changes: 5 additions & 0 deletions cli/src/main/java/org/owasp/dependencycheck/CliParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ private void addAdvancedOptions(final Options options) {
.addOption(newOption(ARGUMENT.DISABLE_PIP, "Disable the pip Analyzer."))
.addOption(newOption(ARGUMENT.DISABLE_PIPFILE, "Disable the Pipfile Analyzer."))
.addOption(newOption(ARGUMENT.DISABLE_COMPOSER, "Disable the PHP Composer Analyzer."))
.addOption(newOption(ARGUMENT.COMPOSER_LOCK_SKIP_DEV, "Configures the PHP Composer Analyzer to skip packages-dev"))
.addOption(newOption(ARGUMENT.DISABLE_CPAN, "Disable the Perl CPAN file Analyzer."))
.addOption(newOption(ARGUMENT.DISABLE_POETRY, "Disable the Poetry Analyzer."))
.addOption(newOption(ARGUMENT.DISABLE_GOLANG_MOD, "Disable the Golang Mod Analyzer."))
Expand Down Expand Up @@ -1249,6 +1250,10 @@ public static class ARGUMENT {
* Disables the PHP Composer Analyzer.
*/
public static final String DISABLE_COMPOSER = "disableComposer";
/**
* Whether the PHP Composer Analyzer skips dev packages.
*/
public static final String COMPOSER_LOCK_SKIP_DEV = "composerSkipDev";
/**
* Disables the Perl CPAN File Analyzer.
*/
Expand Down
1 change: 1 addition & 0 deletions cli/src/site/markdown/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Advanced Options
| | \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp; |
| | \-\-disableJar | | Sets whether the Jar Analyzer will be disabled. | &nbsp; |
| | \-\-disableComposer | | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer will be disabled. | &nbsp; |
| | \-\-composerSkipDev | | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should skip "packages-dev". | &nbsp; |
| | \-\-disableCpan | | Sets whether the [experimental](../analyzers/index.html) Perl CPAN File Analyzer will be disabled. | &nbsp; |
| | \-\-disableDart | | Sets whether the [experimental](../analyzers/index.html) Dart Analyzer will be disabled. | &nbsp; |
| | \-\-disableOssIndex | | Sets whether the [OSS Index Analyzer](../analyzers/oss-index-analyzer.html) will be disabled. This analyzer requires an internet connection. | &nbsp; |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationExcep
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
engine.removeDependency(dependency);
try (FileInputStream fis = new FileInputStream(dependency.getActualFile())) {
final ComposerLockParser clp = new ComposerLockParser(fis);
final boolean skipdev = getSettings().getBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, false);
final ComposerLockParser clp = new ComposerLockParser(fis, skipdev);
LOGGER.debug("Checking composer.lock file {}", dependency.getActualFilePath());
clp.process();
clp.getDependencies().stream().map((dep) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
import javax.annotation.concurrent.NotThreadSafe;

/**
* Parses a Composer.lock file from an input stream. In a separate class so it can hopefully be injected.
* Parses a Composer.lock file from an input stream. In a separate class so it
* can hopefully be injected.
*
* @author colezlaw
*/
Expand All @@ -43,12 +44,14 @@ public class ComposerLockParser {
* The JsonReader for parsing JSON
*/
private final JsonReader jsonReader;

/**
* The List of ComposerDependencies found
*/
private final List<ComposerDependency> composerDependencies;

/**
* Whether to skip dev dependencies.
*/
private final boolean skipDev;
/**
* The LOGGER
*/
Expand All @@ -59,10 +62,11 @@ public class ComposerLockParser {
*
* @param inputStream the InputStream to parse
*/
public ComposerLockParser(InputStream inputStream) {
public ComposerLockParser(InputStream inputStream, boolean skipDev) {
LOGGER.debug("Creating a ComposerLockParser");
this.jsonReader = Json.createReader(inputStream);
this.composerDependencies = new ArrayList<>();
this.skipDev = skipDev;
}

/**
Expand All @@ -76,26 +80,14 @@ public void process() {
LOGGER.debug("Found packages");
final JsonArray packages = composer.getJsonArray("packages");
for (JsonObject pkg : packages.getValuesAs(JsonObject.class)) {
if (pkg.containsKey("name")) {
final String groupName = pkg.getString("name");
if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
if (pkg.containsKey("version")) {
final String group = groupName.substring(0, groupName.indexOf('/'));
final String project = groupName.substring(groupName.indexOf('/') + 1);
String version = pkg.getString("version");
// Some version numbers begin with v - which doesn't end up matching CPE's
if (version.startsWith("v")) {
version = version.substring(1);
}
LOGGER.debug("Got package {}/{}/{}", group, project, version);
composerDependencies.add(new ComposerDependency(group, project, version));
} else {
LOGGER.debug("Group/package {} does not have a version", groupName);
}
} else {
LOGGER.debug("Got a dependency with no name");
}
}
processPackageEntry(pkg);
}
}
if (composer.containsKey("packages-dev") && !skipDev) {
LOGGER.debug("Found packages-dev");
final JsonArray devPackages = composer.getJsonArray("packages-dev");
for (JsonObject pkg : devPackages.getValuesAs(JsonObject.class)) {
processPackageEntry(pkg);
}
}
} catch (JsonParsingException jsonpe) {
Expand All @@ -109,6 +101,29 @@ public void process() {
}
}

protected void processPackageEntry(JsonObject pkg) {
if (pkg.containsKey("name")) {
final String groupName = pkg.getString("name");
if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
if (pkg.containsKey("version")) {
final String group = groupName.substring(0, groupName.indexOf('/'));
final String project = groupName.substring(groupName.indexOf('/') + 1);
String version = pkg.getString("version");
// Some version numbers begin with v - which doesn't end up matching CPE's
if (version.startsWith("v")) {
version = version.substring(1);
}
LOGGER.debug("Got package {}/{}/{}", group, project, version);
composerDependencies.add(new ComposerDependency(group, project, version));
} else {
LOGGER.debug("Group/package {} does not have a version", groupName);
}
} else {
LOGGER.debug("Got a dependency with no name");
}
}
}

/**
* Gets the list of dependencies.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,42 @@ public void setUp() throws Exception {

@Test
public void testValidComposerLock() {
ComposerLockParser clp = new ComposerLockParser(inputStream);
ComposerLockParser clp = new ComposerLockParser(inputStream, false);
clp.process();
assertEquals(30, clp.getDependencies().size());
assertTrue(clp.getDependencies().contains(new ComposerDependency("symfony", "translation", "2.7.3")));
assertTrue(clp.getDependencies().contains(new ComposerDependency("vlucas", "phpdotenv", "1.1.1")));
}


@Test
public void testComposerLockSkipDev() {
ComposerLockParser clp = new ComposerLockParser(inputStream, true);
clp.process();
assertEquals(29, clp.getDependencies().size());
assertTrue(clp.getDependencies().contains(new ComposerDependency("symfony", "translation", "2.7.3")));
//vlucas/phpdotenv is in packages-dev
assertFalse(clp.getDependencies().contains(new ComposerDependency("vlucas", "phpdotenv", "1.1.1")));
}

@Test(expected = ComposerException.class)
public void testNotJSON() throws Exception {
String input = "NOT VALID JSON";
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
clp.process();
}

@Test(expected = ComposerException.class)
public void testNotComposer() throws Exception {
String input = "[\"ham\",\"eggs\"]";
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
clp.process();
}

@Test(expected = ComposerException.class)
public void testNotPackagesArray() throws Exception {
String input = "{\"packages\":\"eleventy\"}";
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
clp.process();
}
}
5 changes: 3 additions & 2 deletions core/src/test/resources/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
*/
@Parameter(property = "composerAnalyzerEnabled")
private Boolean composerAnalyzerEnabled;
/**
* Sets whether or not the PHP Composer Lock File Analyzer will scan "packages-dev".
*/
@Parameter(property = "composerAnalyzerSkipDev")
private boolean composerAnalyzerSkipDev;
/**
* Whether or not the Perl CPAN File Analyzer is enabled.
*/
Expand Down Expand Up @@ -2282,6 +2287,7 @@ protected void populateSettings() {
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PIPFILE_ENABLED, pipfileAnalyzerEnabled);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_POETRY_ENABLED, poetryAnalyzerEnabled);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, composerAnalyzerSkipDev);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CPANFILE_ENABLED, cpanfileAnalyzerEnabled);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED, nodeAuditAnalyzerEnabled);
Expand Down
3 changes: 2 additions & 1 deletion maven/src/site/markdown/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ cmakeAnalyzerEnabled | Sets whether the [experimental](../analyze
autoconfAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) autoconf Analyzer should be used. `enableExperimental` must be set to true. | true
pipAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) pip Analyzer should be used. `enableExperimental` must be set to true. | true
pipfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Pipfile Analyzer should be used. `enableExperimental` must be set to true. | true
poetryAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Poetry Analyzer should be used. `enableExperimental` must be set to true. | true
poetryAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Poetry Analyzer should be used. `enableExperimental` must be set to true. | true
composerAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should be used. `enableExperimental` must be set to true. | true
composerAnalyzerSkipDev | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should skip "packages-dev" | false
cpanfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Perl CPAN File Analyzer should be used. `enableExperimental` must be set to true. | true
yarnAuditAnalyzerEnabled | Sets whether the Yarn Audit Analyzer should be used. This analyzer requires yarn and an internet connection. Use `nodeAuditSkipDevDependencies` to skip dev dependencies. | true
pnpmAuditAnalyzerEnabled | Sets whether the Pnpm Audit Analyzer should be used. This analyzer requires pnpm and an internet connection. Use `nodeAuditSkipDevDependencies` to skip dev dependencies. | true
Expand Down
Loading
Loading