Skip to content

Commit

Permalink
Merge pull request #2 from MSUSEL/ryan-nvd-and-ghsa-clients
Browse files Browse the repository at this point in the history
Remove python port and replace with java-native data access
  • Loading branch information
RyanJCummings authored May 29, 2024
2 parents cec4f07 + 43ed73d commit c113346
Show file tree
Hide file tree
Showing 155 changed files with 2,187 additions and 520,481 deletions.
13 changes: 11 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ hs_err_pid*
replay_pid*

# NVD API key and Github API token
input/keys/*
input/keys/nvd_key.txt
input/keys/github_token.txt
input/credentials/*

# ignore NVD dictionary
src/main/resources/nvd-dictionary.json
Expand All @@ -36,7 +38,14 @@ out/grype.json
out/trivy.json
out/sbomqs.txt

.idea/*
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# sbomqs executable will be installed when docker image is built
src/main/resources/sbomqs
src/main/resources/sbomqs

# ignore .env files
*.env
6 changes: 2 additions & 4 deletions .idea/misc.xml

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

17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,22 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.github.wnameless.json</groupId>
<artifactId>json-flattener</artifactId>
<version>0.16.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>5.0.1</version>
</dependency>
</dependencies>

Expand Down
127 changes: 127 additions & 0 deletions prepare_environment
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/bin/sh


# Copyright 2024 Montana State University Software Engineering and Cybersecurity Laboratory
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the “Software”),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

########################################################################
# Prepares Environment For PIQUE-Cloud and runs PIQUE on sample targets
########################################################################

WORKDIR="$PWD"
INPUT_MESSAGE="##############################################\n# INPUT NEEDED \n##############################################"
PROGNAME="
██████╗ ██╗ ██████╗ ██╗ ██╗███████╗
██╔══██╗██║██╔═══██╗██║ ██║██╔════╝
██████╔╝██║██║ ██║██║ ██║█████╗
██╔═══╝ ██║██║▄▄ ██║██║ ██║██╔══╝
██║ ██║╚██████╔╝╚██████╔╝███████╗
╚═╝ ╚═╝ ╚══▀▀═╝ ╚═════╝ ╚══════╝
██████╗██╗ ██████╗ ██╗ ██╗██████╗
██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗
██║ ██║ ██║ ██║██║ ██║██║ ██║
██║ ██║ ██║ ██║██║ ██║██║ ██║
╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝
╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝
------------------------------------------------------------------------
"
printBanner() {
COLUMNS=$(tput cols)
# shellcheck disable=SC2002
if [ -f msu_logo_ascii.txt ] && [ -x /bin/perl ] && [ "$COLUMNS" -gt 100 ]; then
cat msu_logo_ascii.txt | perl -pe 'select undef,undef,undef,.01555'
printf "$PROGNAME" | perl -pe 'select undef,undef,undef,.01555'
echo " " | perl -pe 'select undef,undef,undef,0.20000'
else
printf "$PROGNAME"
fi
}

# Dependency Checks
check_deps() {
# Check that wget is installed
printf "Checking dependencies...\n"
[ -x "$(which wget)" ] || { printf "\u001b[31mERROR: wget is not installed. Please install wget on your system or perform manual installation of PIQUE-Cloud\nexit 1\n\u001b[0m"; exit 1; }
printf "\twget is installed\n"

# Check that docker is installed and user is logged in
[ -x "$(which docker)" ] || { printf "\u001b[31mPlease ensure that docker is installed, running, and configured with your username and login.\u001b[0m\nexit 1\n"; exit 1; }
printf "\tdocker is installed\n"

# Check git is installed
git --version >/dev/null 2>&1 || { printf "\u001b[31mgit is not installed. Please install git on your system to continue PIQUE-Cloud setup\u001b[0m\nexit 1\n"; exit 1; }
printf "\tgit is installed\n"
printf "\u001b[32mDependency check completed successfully\n\n\u001b[0m"
}

# Check for/create input, output, and subdirectories
set_up_directories() {
printf "Checking directory structure...\n"
[ -d "$WORKDIR/input/keys" ] || { mkdir -p "$WORKDIR/input/keys"; }
[ -d "$WORKDIR/output/" ] || { mkdir -p "$WORKDIR/output"; }
printf "\u001b[32mDirectory check/setup complete\n\n\u001b[0m"
}

# Checks for existing keys. If they don't exist, prompt user to create them and paste into prompts
get_authentication_keys() {
printf "Checking authentication keys...\n"

NVD_KEY_PATH="$WORKDIR/input/keys/nvd-api-key.txt"
GITHUB_TOKEN_PATH="$WORKDIR/input/keys/github-token.txt"

# Check if keys already exist and provide option to paste in keys
# Future Work: Key validation
[ -f "$NVD_KEY_PATH" ] || { printf "$INPUT_MESSAGE\n"; \
printf "\u001b[33mThis step asks you to paste an api key into the terminal. If you are concened about having a secure key in your clipboard or shell history,\n"; \
printf "type the letter 'n' in the prompt. The script will exit and you can manually generate WORKDIR/input/keys/nvd-api-key.txt. Then, rerun this script.\u001b[0m\n"; \
printf "Please generate an API key with the National Vulnerability Database here:\u001b[36m https://nvd.nist.gov/developers/request-an-api-key\u001b[0m\n"; \
printf "Be sure to save it somewhere secure. If you lose the key, you will need to generate a new one.\n\n"; \
printf "Once you have retrieved your key, enter it here: "; \
read nvd_key; \
[ "$nvd_key" = "n" ] && exit 1 || echo "$nvd_key" > $NVD_KEY_PATH; }
printf "\tNVD Key setup complete\n"

# Future Work: Key validation
[ -f "$GITHUB_TOKEN_PATH" ] || { printf "$INPUT_MESSAGE\n"; \
printf "\u001b[33mThis step asks you to paste a personal access token into the terminal. If you are concened about having a secure key in your clipboard or shell \n"; \
printf "history, type the letter 'n' in the prompt. The script will exit and you can manually generate WORKDIR/input/keys/github-token.txt Then, rerun this script.\u001b[0m\n"; \
printf "Please generate a Github personal access token with ......permissions. More info:\u001b[36m https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens\u001b[0m\n\n"; \
printf "Once you have generated or retrieved your token, paste it here: "; \
read -r github_key; \
[ "$github_key" = "n" ] && exit 1 || echo "$github_key" > "$GITHUB_TOKEN_PATH"; }
printf "\tGithub token setup complete\n"
printf "\u001b[32mAuthentication setup complete\u001b[0m\n\n"
}

run() {
printBanner
check_deps
set_up_directories
get_authentication_keys

# docker run -it --rm -v "/var/run/docker.sock:/var/run/docker.sock:rw" -v "$WORKDIR"/input:/input -v "$WORKDIR"/output:/output msusel/pique-sbom-supply-chain-sec:latest
docker-compose -f "$WORKDIR/src/main/java/data/nvdMirror/docker-compose.yml" up --detach
}
run

72 changes: 72 additions & 0 deletions src/main/java/data/GHSARequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package data;

import data.baseClasses.BaseRequest;
import data.handlers.SecurityAdvisoryMarshaller;
import data.handlers.JsonResponseHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class GHSARequest extends BaseRequest {
private static final Logger LOGGER = LoggerFactory.getLogger(GHSARequest.class);
private final JsonResponseHandler handler = new JsonResponseHandler();
private final String query;

public GHSARequest(String httpMethod, String baseURI, List<String> headers, String query) {
super(httpMethod, baseURI, headers);
this.query = query;
}

@Override
public GHSAResponse executeRequest() {
return executeGHSARequest();
}

private GHSAResponse executeGHSARequest() {
URI uri;
GHSAResponse ghsaResponse = new GHSAResponse();
SecurityAdvisoryMarshaller securityAdvisoryMarshaler = new SecurityAdvisoryMarshaller();

try {
uri = new URIBuilder(baseURI).build();
} catch (URISyntaxException e) {
LOGGER.error("Could not build URI with given inputs", e);
throw new RuntimeException(e);
}

HttpPost request = new HttpPost();
request.setURI(uri);
request.setHeaders(Utils.resolveHeaders(headers));
request.setEntity(new StringEntity(query, StandardCharsets.UTF_8));

try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(request)) {

int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
String json = handler.handleResponse(response);
ghsaResponse.setSecurityAdvisory(securityAdvisoryMarshaler.unmarshallJson(json));
ghsaResponse.setStatus(status);
} else {
LOGGER.info("Response Status: {}", status);
throw new IOException("Failed to execute request: " + response.getStatusLine());
}
} catch ( IOException e) {
LOGGER.info("Request failed", e);
throw new RuntimeException(e);
}

return ghsaResponse;
}
}
12 changes: 12 additions & 0 deletions src/main/java/data/GHSAResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package data;

import data.baseClasses.BaseResponse;
import data.ghsaData.SecurityAdvisory;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GHSAResponse extends BaseResponse {
private SecurityAdvisory securityAdvisory;
}
5 changes: 5 additions & 0 deletions src/main/java/data/GraphQlQueries.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package data;

public class GraphQlQueries {
public static final String GHSA_SECURITY_ADVISORY_QUERY = "query { securityAdvisory(ghsaId: \"%s\") { ghsaId summary cwes(first : 1) { nodes { cweId } } } }";
}
80 changes: 80 additions & 0 deletions src/main/java/data/MongoConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package data;

import com.mongodb.MongoClientException;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import utilities.helperFunctions;

import java.io.IOException;
import java.util.*;

/**
* Creates and manages a Singleton Mongo Connection
*/
public class MongoConnection {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoConnection.class);
private static volatile MongoClient mongoClient = null;

private MongoConnection() {}

/**
* Builds a MongoClient instance if one does not already exist.
* This uses the MongoClient class to manage the connection pool.
*
* @return existing MongoClient or a new one if one does not already exist
*/
public static MongoClient getInstance() {
if (mongoClient == null) {
synchronized (MongoClient.class) {
if (mongoClient == null) {
mongoClient = MongoClients.create("mongodb://localhost:27017");
}
}
}
return mongoClient;
}

// public static MongoClient getInstance() {
// String username, password, port, hostname, dbname;
// Map<String, String> credentials;
//
// try {
// credentials = helperFunctions.getMongoCredentials();
// } catch (IOException e) {
// LOGGER.error("Could not read credentials file", e);
// throw new RuntimeException(e);
// }
//
// username = credentials.get("username");
// password = credentials.get("password");
// hostname = credentials.get("hostname");
// port = credentials.get("port");
// dbname = credentials.get("dbname");
//
// if (mongoClient == null) {
// synchronized (MongoClient.class) {
// if (mongoClient == null) {
// MongoCredential credential = MongoCredential.createCredential(username, dbname, password.toCharArray());
// try {
// mongoClient = MongoClients.create(
// MongoClientSettings.builder()
// .applyToClusterSettings(builder ->
// builder.hosts(Arrays.asList(new ServerAddress(hostname, Integer.parseInt(port)))))
// .credential(credential)
// .build());
// } catch (MongoClientException e) {
// LOGGER.error(String.format("Could not connect to Mongo database: {%s}.", dbname), e);
// throw new RuntimeException(e);
// }
// }
// }
// }
//
// return mongoClient;
// }
}
Loading

0 comments on commit c113346

Please sign in to comment.