-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support Pipenv - Pipfile.lock (#5404)
- Loading branch information
1 parent
c0fcd40
commit 4730296
Showing
13 changed files
with
1,184 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
219 changes: 219 additions & 0 deletions
219
core/src/main/java/org/owasp/dependencycheck/analyzer/PipfilelockAnalyzer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
/* | ||
* This file is part of dependency-check-core. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* Copyright (c) 2023 The OWASP Foundation. All Rights Reserved. | ||
*/ | ||
package org.owasp.dependencycheck.analyzer; | ||
|
||
import org.apache.commons.io.FileUtils; | ||
import com.github.packageurl.MalformedPackageURLException; | ||
import com.github.packageurl.PackageURL; | ||
import com.github.packageurl.PackageURLBuilder; | ||
import java.io.BufferedInputStream; | ||
import org.owasp.dependencycheck.Engine; | ||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; | ||
import org.owasp.dependencycheck.dependency.Confidence; | ||
import org.owasp.dependencycheck.dependency.Dependency; | ||
import org.owasp.dependencycheck.utils.FileFilterBuilder; | ||
import org.owasp.dependencycheck.utils.Settings; | ||
import org.owasp.dependencycheck.dependency.EvidenceType; | ||
import org.owasp.dependencycheck.exception.InitializationException; | ||
import org.owasp.dependencycheck.dependency.naming.GenericIdentifier; | ||
import org.owasp.dependencycheck.dependency.naming.PurlIdentifier; | ||
import org.owasp.dependencycheck.utils.Checksum; | ||
|
||
import java.io.File; | ||
import java.io.FileFilter; | ||
import java.io.FileInputStream; | ||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.Charset; | ||
import java.util.Set; | ||
import javax.annotation.concurrent.ThreadSafe; | ||
import javax.json.Json; | ||
import javax.json.JsonException; | ||
import javax.json.JsonObject; | ||
import javax.json.JsonReader; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Used to analyze dependencies defined in Pipfile.lock. This analyzer works in | ||
* tandem with the `PipfileAnalyzer` - and both analyzers use the same key to | ||
* enable/disable the analyzers. The PipfileAnalyzer will be skipped if the lock | ||
* file exists, as the lock will provide more accurate version numbers. | ||
* | ||
* @author jeremy.long | ||
*/ | ||
@Experimental | ||
@ThreadSafe | ||
public class PipfilelockAnalyzer extends AbstractFileTypeAnalyzer { | ||
|
||
/** | ||
* The logger. | ||
*/ | ||
private static final Logger LOGGER = LoggerFactory.getLogger(PipfilelockAnalyzer.class); | ||
/** | ||
* "Pipfile.lock" file. | ||
*/ | ||
private static final String LOCKFILE = "Pipfile.lock"; | ||
|
||
/** | ||
* The identifiedPackage of the analyzer. | ||
*/ | ||
private static final String ANALYZER_NAME = "Pipfile.lock Analyzer"; | ||
|
||
/** | ||
* The phase that this analyzer is intended to run in. | ||
*/ | ||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; | ||
|
||
/** | ||
* The file filter used to determine which files this analyzer supports. | ||
*/ | ||
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames(LOCKFILE).build(); | ||
|
||
/** | ||
* Returns the FileFilter | ||
* | ||
* @return the FileFilter | ||
*/ | ||
@Override | ||
protected FileFilter getFileFilter() { | ||
return FILTER; | ||
} | ||
|
||
/** | ||
* Returns the identifiedPackage of the analyzer. | ||
* | ||
* @return the identifiedPackage of the analyzer. | ||
*/ | ||
@Override | ||
public String getName() { | ||
return ANALYZER_NAME; | ||
} | ||
|
||
/** | ||
* Returns the phase that the analyzer is intended to run in. | ||
* | ||
* @return the phase that the analyzer is intended to run in. | ||
*/ | ||
@Override | ||
public AnalysisPhase getAnalysisPhase() { | ||
return ANALYSIS_PHASE; | ||
} | ||
|
||
/** | ||
* Returns the key used in the properties file to reference the analyzer's | ||
* enabled property. | ||
* | ||
* @return the analyzer's enabled property setting key | ||
*/ | ||
@Override | ||
protected String getAnalyzerEnabledSettingKey() { | ||
return Settings.KEYS.ANALYZER_PIPFILE_ENABLED; | ||
} | ||
|
||
@Override | ||
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { | ||
LOGGER.debug("Checking file {}", dependency.getActualFilePath()); | ||
|
||
engine.removeDependency(dependency); | ||
|
||
final File dependencyFile = dependency.getActualFile(); | ||
if (!dependencyFile.isFile() || dependencyFile.length() == 0) { | ||
return; | ||
} | ||
|
||
final File actualFile = dependency.getActualFile(); | ||
try (FileInputStream fin = new FileInputStream(actualFile); | ||
InputStream in = new BufferedInputStream(fin); | ||
JsonReader jsonReader = Json.createReader(in)) { | ||
|
||
final JsonObject jsonLock = jsonReader.readObject(); | ||
final JsonObject develop = jsonLock.getJsonObject("develop"); | ||
final Set<String> keys = develop.keySet(); | ||
for (String identifiedPackage : keys) { | ||
final JsonObject dep = develop.getJsonObject(identifiedPackage); | ||
final String selectedVersion = dep.getString("version", "").trim(); | ||
if (selectedVersion.startsWith("==") && selectedVersion.length() > 2) { | ||
final String identifiedVersion = selectedVersion.substring(2).trim(); | ||
LOGGER.debug("package, version: {} {}", identifiedPackage, identifiedVersion); | ||
|
||
final Dependency d = new Dependency(dependency.getActualFile(), true); | ||
d.setName(identifiedPackage); | ||
d.setVersion(identifiedVersion); | ||
try { | ||
final PackageURL purl = PackageURLBuilder.aPackageURL() | ||
.withType("pypi") | ||
.withName(identifiedPackage) | ||
.withVersion(identifiedVersion) | ||
.build(); | ||
d.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST)); | ||
} catch (MalformedPackageURLException ex) { | ||
LOGGER.debug("Unable to build package url for pypi", ex); | ||
d.addSoftwareIdentifier(new GenericIdentifier("pypi:" + identifiedPackage + "@" + identifiedVersion, Confidence.HIGH)); | ||
} | ||
d.setPackagePath(String.format("%s:%s", identifiedPackage, identifiedVersion)); | ||
d.setEcosystem(PythonDistributionAnalyzer.DEPENDENCY_ECOSYSTEM); | ||
final String filePath = String.format("%s:%s/%s", dependency.getFilePath(), identifiedPackage, identifiedVersion); | ||
d.setFilePath(filePath); | ||
d.setSha1sum(Checksum.getSHA1Checksum(filePath)); | ||
d.setSha256sum(Checksum.getSHA256Checksum(filePath)); | ||
d.setMd5sum(Checksum.getMD5Checksum(filePath)); | ||
d.addEvidence(EvidenceType.VENDOR, LOCKFILE, "vendor", identifiedPackage, Confidence.HIGHEST); | ||
d.addEvidence(EvidenceType.PRODUCT, LOCKFILE, "product", identifiedPackage, Confidence.HIGHEST); | ||
d.addEvidence(EvidenceType.VERSION, LOCKFILE, "version", identifiedVersion, Confidence.HIGHEST); | ||
engine.addDependency(d); | ||
} else { | ||
LOGGER.debug("Skipping `{}`: Unknown version `{}` in `{}`", identifiedPackage, selectedVersion, dependency.getActualFilePath()); | ||
} | ||
} | ||
} catch (FileNotFoundException ex) { | ||
throw new RuntimeException(ex); | ||
} catch (IOException | JsonException ex) { | ||
throw new RuntimeException(ex); | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves the contents of a given file. | ||
* | ||
* @param actualFile the file to read | ||
* @return the contents of the file | ||
* @throws AnalysisException thrown if there is an IO Exception | ||
*/ | ||
private String getFileContents(final File actualFile) throws AnalysisException { | ||
try { | ||
return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim(); | ||
} catch (IOException e) { | ||
throw new AnalysisException("Problem occurred while reading dependency file.", e); | ||
} | ||
} | ||
|
||
/** | ||
* Initializes the file type analyzer. | ||
* | ||
* @param engine a reference to the dependency-check engine | ||
* @throws InitializationException thrown if there is an exception during | ||
* initialization | ||
*/ | ||
@Override | ||
protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { | ||
// No initialization needed. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.