Skip to content

Commit 08d7657

Browse files
authored
feat: PHP Composer Analyzer Scans packages-dev by default (#7114)
1 parent 910570d commit 08d7657

File tree

13 files changed

+106
-32
lines changed

13 files changed

+106
-32
lines changed

ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,10 @@ public class Check extends Update {
308308
* Whether or not the PHP Composer Analyzer is enabled.
309309
*/
310310
private Boolean composerAnalyzerEnabled;
311+
/**
312+
* Whether or not the PHP Composer Analyzer will skip "packages-dev".
313+
*/
314+
private Boolean composerAnalyzerSkipDev;
311315
/**
312316
* Whether or not the Perl CPAN File Analyzer is enabled.
313317
*/
@@ -943,6 +947,24 @@ public Boolean isComposerAnalyzerEnabled() {
943947
public void setComposerAnalyzerEnabled(Boolean composerAnalyzerEnabled) {
944948
this.composerAnalyzerEnabled = composerAnalyzerEnabled;
945949
}
950+
951+
/**
952+
* Get the value of composerAnalyzerSkipDev.
953+
*
954+
* @return the value of composerAnalyzerSkipDev
955+
*/
956+
public Boolean isComposerAnalyzerSkipDev() {
957+
return composerAnalyzerSkipDev;
958+
}
959+
960+
/**
961+
* Set the value of composerAnalyzerSkipDev.
962+
*
963+
* @param composerAnalyzerSkipDev new value of composerAnalyzerSkipDev
964+
*/
965+
public void setComposerAnalyzerSkipDev(Boolean composerAnalyzerSkipDev) {
966+
this.composerAnalyzerSkipDev = composerAnalyzerSkipDev;
967+
}
946968

947969
/**
948970
* Get the value of cpanfileAnalyzerEnabled.
@@ -2183,6 +2205,7 @@ protected void populateSettings() throws BuildException {
21832205
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_PIPFILE_ENABLED, pipfileAnalyzerEnabled);
21842206
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_POETRY_ENABLED, poetryAnalyzerEnabled);
21852207
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
2208+
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, composerAnalyzerSkipDev);
21862209
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_CPANFILE_ENABLED, cpanfileAnalyzerEnabled);
21872210
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
21882211
getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_SKIPDEV, nodePackageSkipDevDependencies);

ant/src/site/markdown/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pipAnalyzerEnabled | Sets whether the [experimental](../analyze
105105
pipfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Pipfile Analyzer should be used. `enableExperimental` must be set to true. | true
106106
poetryAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Poetry Analyzer should be used. `enableExperimental` must be set to true. | true
107107
composerAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should be used. `enableExperimental` must be set to true. | true
108+
composerAnalyzerSkipDev | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should skip "packages-dev" | false
108109
cpanfileAnalyzerEnabled | Sets whether the [experimental](../analyzers/index.html) Perl CPAN File Analyzer should be used. `enableExperimental` must be set to true. | true
109110
nodeAnalyzerEnabled | Sets whether the [retired](../analyzers/index.html) Node.js Analyzer should be used. | true
110111
nodeAuditAnalyzerEnabled | Sets whether the Node Audit Analyzer should be used. This analyzer requires an internet connection. | true

cli/src/main/java/org/owasp/dependencycheck/App.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,8 @@ protected void populateSettings(CliParser cli) throws InvalidSettingException {
553553
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_OPENSSL, Settings.KEYS.ANALYZER_OPENSSL_ENABLED));
554554
settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED,
555555
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_COMPOSER, Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED));
556+
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV,
557+
cli.hasOption(CliParser.ARGUMENT.COMPOSER_LOCK_SKIP_DEV));
556558
settings.setBoolean(Settings.KEYS.ANALYZER_CPANFILE_ENABLED,
557559
!cli.isDisabled(CliParser.ARGUMENT.DISABLE_CPAN, Settings.KEYS.ANALYZER_CPANFILE_ENABLED));
558560
settings.setBoolean(Settings.KEYS.ANALYZER_GOLANG_DEP_ENABLED,

cli/src/main/java/org/owasp/dependencycheck/CliParser.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ private void addAdvancedOptions(final Options options) {
494494
.addOption(newOption(ARGUMENT.DISABLE_PIP, "Disable the pip Analyzer."))
495495
.addOption(newOption(ARGUMENT.DISABLE_PIPFILE, "Disable the Pipfile Analyzer."))
496496
.addOption(newOption(ARGUMENT.DISABLE_COMPOSER, "Disable the PHP Composer Analyzer."))
497+
.addOption(newOption(ARGUMENT.COMPOSER_LOCK_SKIP_DEV, "Configures the PHP Composer Analyzer to skip packages-dev"))
497498
.addOption(newOption(ARGUMENT.DISABLE_CPAN, "Disable the Perl CPAN file Analyzer."))
498499
.addOption(newOption(ARGUMENT.DISABLE_POETRY, "Disable the Poetry Analyzer."))
499500
.addOption(newOption(ARGUMENT.DISABLE_GOLANG_MOD, "Disable the Golang Mod Analyzer."))
@@ -1249,6 +1250,10 @@ public static class ARGUMENT {
12491250
* Disables the PHP Composer Analyzer.
12501251
*/
12511252
public static final String DISABLE_COMPOSER = "disableComposer";
1253+
/**
1254+
* Whether the PHP Composer Analyzer skips dev packages.
1255+
*/
1256+
public static final String COMPOSER_LOCK_SKIP_DEV = "composerSkipDev";
12521257
/**
12531258
* Disables the Perl CPAN File Analyzer.
12541259
*/

cli/src/site/markdown/arguments.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Advanced Options
7474
| | \-\-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; |
7575
| | \-\-disableJar | | Sets whether the Jar Analyzer will be disabled. | &nbsp; |
7676
| | \-\-disableComposer | | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer will be disabled. | &nbsp; |
77+
| | \-\-composerSkipDev | | Sets whether the [experimental](../analyzers/index.html) PHP Composer Lock File Analyzer should skip "packages-dev". | &nbsp; |
7778
| | \-\-disableCpan | | Sets whether the [experimental](../analyzers/index.html) Perl CPAN File Analyzer will be disabled. | &nbsp; |
7879
| | \-\-disableDart | | Sets whether the [experimental](../analyzers/index.html) Dart Analyzer will be disabled. | &nbsp; |
7980
| | \-\-disableOssIndex | | Sets whether the [OSS Index Analyzer](../analyzers/oss-index-analyzer.html) will be disabled. This analyzer requires an internet connection. | &nbsp; |

core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationExcep
108108
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
109109
engine.removeDependency(dependency);
110110
try (FileInputStream fis = new FileInputStream(dependency.getActualFile())) {
111-
final ComposerLockParser clp = new ComposerLockParser(fis);
111+
final boolean skipdev = getSettings().getBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, false);
112+
final ComposerLockParser clp = new ComposerLockParser(fis, skipdev);
112113
LOGGER.debug("Checking composer.lock file {}", dependency.getActualFilePath());
113114
clp.process();
114115
clp.getDependencies().stream().map((dep) -> {

core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
import javax.annotation.concurrent.NotThreadSafe;
3333

3434
/**
35-
* Parses a Composer.lock file from an input stream. In a separate class so it can hopefully be injected.
35+
* Parses a Composer.lock file from an input stream. In a separate class so it
36+
* can hopefully be injected.
3637
*
3738
* @author colezlaw
3839
*/
@@ -43,12 +44,14 @@ public class ComposerLockParser {
4344
* The JsonReader for parsing JSON
4445
*/
4546
private final JsonReader jsonReader;
46-
4747
/**
4848
* The List of ComposerDependencies found
4949
*/
5050
private final List<ComposerDependency> composerDependencies;
51-
51+
/**
52+
* Whether to skip dev dependencies.
53+
*/
54+
private final boolean skipDev;
5255
/**
5356
* The LOGGER
5457
*/
@@ -59,10 +62,11 @@ public class ComposerLockParser {
5962
*
6063
* @param inputStream the InputStream to parse
6164
*/
62-
public ComposerLockParser(InputStream inputStream) {
65+
public ComposerLockParser(InputStream inputStream, boolean skipDev) {
6366
LOGGER.debug("Creating a ComposerLockParser");
6467
this.jsonReader = Json.createReader(inputStream);
6568
this.composerDependencies = new ArrayList<>();
69+
this.skipDev = skipDev;
6670
}
6771

6872
/**
@@ -76,26 +80,14 @@ public void process() {
7680
LOGGER.debug("Found packages");
7781
final JsonArray packages = composer.getJsonArray("packages");
7882
for (JsonObject pkg : packages.getValuesAs(JsonObject.class)) {
79-
if (pkg.containsKey("name")) {
80-
final String groupName = pkg.getString("name");
81-
if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
82-
if (pkg.containsKey("version")) {
83-
final String group = groupName.substring(0, groupName.indexOf('/'));
84-
final String project = groupName.substring(groupName.indexOf('/') + 1);
85-
String version = pkg.getString("version");
86-
// Some version numbers begin with v - which doesn't end up matching CPE's
87-
if (version.startsWith("v")) {
88-
version = version.substring(1);
89-
}
90-
LOGGER.debug("Got package {}/{}/{}", group, project, version);
91-
composerDependencies.add(new ComposerDependency(group, project, version));
92-
} else {
93-
LOGGER.debug("Group/package {} does not have a version", groupName);
94-
}
95-
} else {
96-
LOGGER.debug("Got a dependency with no name");
97-
}
98-
}
83+
processPackageEntry(pkg);
84+
}
85+
}
86+
if (composer.containsKey("packages-dev") && !skipDev) {
87+
LOGGER.debug("Found packages-dev");
88+
final JsonArray devPackages = composer.getJsonArray("packages-dev");
89+
for (JsonObject pkg : devPackages.getValuesAs(JsonObject.class)) {
90+
processPackageEntry(pkg);
9991
}
10092
}
10193
} catch (JsonParsingException jsonpe) {
@@ -109,6 +101,29 @@ public void process() {
109101
}
110102
}
111103

104+
protected void processPackageEntry(JsonObject pkg) {
105+
if (pkg.containsKey("name")) {
106+
final String groupName = pkg.getString("name");
107+
if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
108+
if (pkg.containsKey("version")) {
109+
final String group = groupName.substring(0, groupName.indexOf('/'));
110+
final String project = groupName.substring(groupName.indexOf('/') + 1);
111+
String version = pkg.getString("version");
112+
// Some version numbers begin with v - which doesn't end up matching CPE's
113+
if (version.startsWith("v")) {
114+
version = version.substring(1);
115+
}
116+
LOGGER.debug("Got package {}/{}/{}", group, project, version);
117+
composerDependencies.add(new ComposerDependency(group, project, version));
118+
} else {
119+
LOGGER.debug("Group/package {} does not have a version", groupName);
120+
}
121+
} else {
122+
LOGGER.debug("Got a dependency with no name");
123+
}
124+
}
125+
}
126+
112127
/**
113128
* Gets the list of dependencies.
114129
*

core/src/test/java/org/owasp/dependencycheck/data/composer/ComposerLockParserTest.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,42 @@ public void setUp() throws Exception {
4343

4444
@Test
4545
public void testValidComposerLock() {
46-
ComposerLockParser clp = new ComposerLockParser(inputStream);
46+
ComposerLockParser clp = new ComposerLockParser(inputStream, false);
4747
clp.process();
4848
assertEquals(30, clp.getDependencies().size());
4949
assertTrue(clp.getDependencies().contains(new ComposerDependency("symfony", "translation", "2.7.3")));
50+
assertTrue(clp.getDependencies().contains(new ComposerDependency("vlucas", "phpdotenv", "1.1.1")));
51+
}
52+
53+
54+
@Test
55+
public void testComposerLockSkipDev() {
56+
ComposerLockParser clp = new ComposerLockParser(inputStream, true);
57+
clp.process();
58+
assertEquals(29, clp.getDependencies().size());
59+
assertTrue(clp.getDependencies().contains(new ComposerDependency("symfony", "translation", "2.7.3")));
60+
//vlucas/phpdotenv is in packages-dev
61+
assertFalse(clp.getDependencies().contains(new ComposerDependency("vlucas", "phpdotenv", "1.1.1")));
5062
}
5163

5264
@Test(expected = ComposerException.class)
5365
public void testNotJSON() throws Exception {
5466
String input = "NOT VALID JSON";
55-
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
67+
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
5668
clp.process();
5769
}
5870

5971
@Test(expected = ComposerException.class)
6072
public void testNotComposer() throws Exception {
6173
String input = "[\"ham\",\"eggs\"]";
62-
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
74+
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
6375
clp.process();
6476
}
6577

6678
@Test(expected = ComposerException.class)
6779
public void testNotPackagesArray() throws Exception {
6880
String input = "{\"packages\":\"eleventy\"}";
69-
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())));
81+
ComposerLockParser clp = new ComposerLockParser(new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), false);
7082
clp.process();
7183
}
7284
}

core/src/test/resources/composer.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
490490
*/
491491
@Parameter(property = "composerAnalyzerEnabled")
492492
private Boolean composerAnalyzerEnabled;
493+
/**
494+
* Sets whether or not the PHP Composer Lock File Analyzer will scan "packages-dev".
495+
*/
496+
@Parameter(property = "composerAnalyzerSkipDev")
497+
private boolean composerAnalyzerSkipDev;
493498
/**
494499
* Whether or not the Perl CPAN File Analyzer is enabled.
495500
*/
@@ -2282,6 +2287,7 @@ protected void populateSettings() {
22822287
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PIPFILE_ENABLED, pipfileAnalyzerEnabled);
22832288
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_POETRY_ENABLED, poetryAnalyzerEnabled);
22842289
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
2290+
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_SKIP_DEV, composerAnalyzerSkipDev);
22852291
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CPANFILE_ENABLED, cpanfileAnalyzerEnabled);
22862292
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
22872293
settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED, nodeAuditAnalyzerEnabled);

0 commit comments

Comments
 (0)