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

Feature/support for cargo #30

Merged
merged 2 commits into from
Mar 8, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ jshell.history
/target-old/
nbactions.xml
nb-configuration.xml
/plugin.cargo/nbproject/
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public static String getValue(String keyPath,String json){
return null;
}

public static List<String> getValues(String keyPath,String json){
List<String> res = new ArrayList<>();
public static List<Pair<String,String>> getValues(String keyPath,String json){
List<Pair<String,String>> res = new ArrayList<>();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.createObjectNode();
try {
Expand All @@ -63,7 +63,7 @@ public static List<String> getValues(String keyPath,String json){
Iterator it = myNode.fields();
while(it.hasNext()){
Map.Entry<String,JsonNode> field = (Map.Entry<String,JsonNode>) it.next();
res.add(field.getKey());
res.add(new Pair<>(field.getKey(),field.getValue().toPrettyString()));
}
return res;
} catch (Exception ex) {
Expand All @@ -72,6 +72,24 @@ public static List<String> getValues(String keyPath,String json){
return null;
}

public static List<String> getArray(String keyPath, String json) {
List<String> res = new ArrayList<>();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.createObjectNode();
try {
root = objectMapper.readTree(json);
JsonNode myNode = root.at(keyPath);
if(myNode.isArray()) {
myNode.forEach(obj -> res.add(obj.toString()));
return res;
}
throw new Exception();
} catch (Exception ex) {
CLIHelper.updateCurrentLine("Failed to parse json!", Ansi.Color.RED);
}
return null;
}

public static String createJSONObjectString(ArrayList<Pair<String,String>> kvs){
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
Expand Down Expand Up @@ -75,6 +74,17 @@ public static String postData(String destUrl, String payload) {

public static String getData(String endpoint) {
String response = "NO_RESPONSE";
try {
URL url = new URL(endpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
StringWriter writer = new StringWriter();
IOUtils.copy(connection.getInputStream(),writer,Charset.forName("UTF-8"));
response = writer.toString();

}catch(Exception e) {

}
return response;
}

Expand Down
1 change: 1 addition & 0 deletions dependency-scanner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<module>../plugin.maven</module>
<module>../plugin.npm</module>
<module>../phsyberdome-sca-cli</module>
<module>../plugin.cargo</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
5 changes: 5 additions & 0 deletions phsyberdome-sca-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@
<artifactId>plugin.npm</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>plugin.cargo</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
com.phsyberdome.plugin.maven.PluginMaven
com.phsyberdome.plugin.npm.PluginNpm
com.phsyberdome.plugin.npm.PluginNpm
com.phsyberdome.plugin.cargo.PluginCargo
44 changes: 44 additions & 0 deletions plugin.cargo/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.phsyberdome</groupId>
<artifactId>plugin.cargo</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>common-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.moandjiezana.toml</groupId>
<artifactId>toml4j</artifactId>
<version>0.7.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<exec.mainClass>com.phsyberdome.plugin.cargo.PluginCargo</exec.mainClass>
</properties>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

package com.phsyberdome.plugin.cargo;

/**
*
* @author Pratham Gahlout
*/
public class CargoVersionHelper {

/*
https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
*/

private static boolean isResolved(String v) {
return true;
}


public static String resolveVersion(String reqSpec) {
return reqSpec;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@

package com.phsyberdome.plugin.cargo;

import com.moandjiezana.toml.Toml;
import com.phsyberdome.common.interfaces.LicenseDetectorInterface;
import com.phsyberdome.common.interfaces.PluginInterface;
import com.phsyberdome.common.utils.CLIHelper;
import com.phsyberdome.common.utils.FileUtil;
import com.phsyberdome.common.utils.models.DependencyManager;
import com.phsyberdome.common.utils.models.Module;
import com.phsyberdome.common.utils.models.Pair;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.fusesource.jansi.Ansi;

/**
*
* @author Pratham Gahlout
*/
public class PluginCargo implements PluginInterface{

private LicenseDetectorInterface licenseDetector;
private Path rootPath;
private Path cloneLocation;
private DependencyManager packageManager;

private ArrayList<Module> modules;

private List<Module> scannedDependencies;

private String cargoTomlFileNameRegex = "(?i)cargo\\.toml";


public PluginCargo() {
this.scannedDependencies = new ArrayList<>();
}

public PluginCargo(LicenseDetectorInterface licenseDetector) {
this.licenseDetector = licenseDetector;
this.scannedDependencies = new ArrayList<>();
}

@Override
public void setLicenseDetector(LicenseDetectorInterface detector) {
this.licenseDetector = detector;
}

@Override
public void setRootPath(Path path) {
this.rootPath = path;
}

@Override
public void setCloneLocation(Path cloneLocation) {
this.cloneLocation = cloneLocation;
}

@Override
public DependencyManager getPackageManager() {
return this.packageManager;
}

@Override
public ArrayList<Module> readModules() {
modules = new ArrayList<>();
this.packageManager = new DependencyManager("Cargo","null");

/*
Read Cargo.toml file
*/
File tomlFile = FileUtil.searchFile(this.rootPath.toFile(), cargoTomlFileNameRegex);

if(tomlFile == null) {
// I guess this is not a rust project
CLIHelper.printLine("Couldn't find any cargo.toml", Ansi.Color.YELLOW);
return modules;
}

Toml toml = new Toml().read(tomlFile);

String moduleTitle = toml.getString("title", "N/A");
String version = toml.getString("version","null");

Toml dependencies = toml.getTable("dependencies");

Module root = new Module(moduleTitle, version);

dependencies.entrySet()
.stream()
.forEach(
dep -> {
String name = dep.getKey();
String ver = dep.getValue().toString();

// Resolve version before proceeding
String resolvedVersion = CargoVersionHelper.resolveVersion(ver);
Module module = new Module(name,resolvedVersion);
resolveDependencyTree(module);
root.addToDependencies(module);
}
);
modules.add(root);

return modules;
}

private void resolveDependencyTree(Module module) {
String license = RegistryHelper.getLicenseFromRegistry(module.getName(), module.getVersion());
if(license.isEmpty()) {
// Try to see if there is an open source url? If yes then analyze that
String repoUrl = RegistryHelper.getSourceCodeRepoLink(module.getName());
if(!repoUrl.isEmpty()) {
Path path = FileUtil.getFilePathFromURL(repoUrl,this.cloneLocation.toString());
if(path!=null){
license = licenseDetector.detect(path.toString()).first;
}
}
}
module.setLicense(license);
// Get dependencies declared in the registry against this version
List<Pair<String,String>> depData = RegistryHelper.getDependenciesOfCrate(module.getName(),module.getVersion());
for(var dep: depData) {
String resolvedVersion = CargoVersionHelper.resolveVersion(dep.second);
Module m = new Module(dep.first,resolvedVersion);
// Recurse
resolveDependencyTree(m);
module.addToDependencies(m);
}

}

@Override
public ArrayList<Module> getModules() {
return modules != null ? modules : new ArrayList<>();
}




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

package com.phsyberdome.plugin.cargo;

import com.phsyberdome.common.utils.JSONHelper;
import com.phsyberdome.common.utils.NetworkHelper;
import com.phsyberdome.common.utils.models.Pair;
import java.util.ArrayList;
import java.util.List;

/**
*
* @author Pratham Gahlout
*/
public class RegistryHelper {

public static final String CRATE_API_BASE = "https://crates.io/api/v1/crates/";



public static String getBaseCrateUrl(String pkgName) {
return CRATE_API_BASE + pkgName;
}

public static String getCrateUrl(String pkgName, String version) {

return getBaseCrateUrl(pkgName) + "/" + version;
}

public static List<String> getAllVersionsOfCrate(String pkgName) {
String crateUrl = getBaseCrateUrl(pkgName);
String metadata = NetworkHelper.getData(crateUrl);
List<String> versions = JSONHelper.getArray("/versions", metadata);


return versions == null ? new ArrayList<>() : versions
.stream()
.map(
version -> JSONHelper.getValue("/num", version)
).toList();

}

// This will always be prioritized unless it returns empty handed
public static String getLicenseFromRegistry(String pkgName,String version) {
String crateUrl = getCrateUrl(pkgName,version);
String metadata = NetworkHelper.getData(crateUrl);


String license = JSONHelper.getValue("/version/license",metadata);
return license != null ? license : "";
}

public static String getSourceCodeRepoLink(String pkgName) {
String crateUrl = getBaseCrateUrl(pkgName);
String data = NetworkHelper.getData(crateUrl);
String repoUrl = JSONHelper.getValue("/crate/repository", data);

return repoUrl != null ? repoUrl : "";
}

// Returns dependency name and version requirement spec
public static List<Pair<String,String>> getDependenciesOfCrate(String pkgName, String version) {
String crateDependencyUrl = getCrateUrl(pkgName,version) + "/dependencies";
String data = NetworkHelper.getData(crateDependencyUrl);
List<String> dependencies = JSONHelper.getArray("/dependencies",data);

return dependencies == null ? new ArrayList<>() :
dependencies.stream()
.map(
dep -> {
String name = JSONHelper.getValue("/crate_id",dep);
String req = JSONHelper.getValue("/req", dep);
return new Pair<String,String>(name,req);
}
).toList();
}
}
Loading
Loading