Skip to content
Open
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ $ DATA_DIR=BH_DATA bloodhound-ce
Then the data will be stored in `BH_DATA` in the current working directory.
The port to listen on can similarly be changed by setting `$PORT`.

## Neo4j GDS Plugin

By default, the script installs the Neo4j Graph Data Science (GDS) plugin,
which is required for certain BloodHound features. If you want to disable this:

```console
$ INSTALL_GDS=false bloodhound-ce
```

To update the images:

```console
Expand Down
70 changes: 70 additions & 0 deletions bloodhound-ce
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/BloodHound-CE/$WORKSPACE"
NEO4J_VOL="$DATA_DIR/neo4j"
POSTGRES_VOL="$DATA_DIR/postgres"
NETWORK="BloodHound-CE-network"
INSTALL_GDS="${INSTALL_GDS:-true}"

BLOODHOUND_ADMIN_NAME="admin"
BLOODHOUND_ADMIN_PASSWORD="admin"
Expand Down Expand Up @@ -88,6 +89,73 @@ function run_postgres() {
>/dev/null
}

function install_gds_plugin() {
if [ "$INSTALL_GDS" = "true" ]; then
local PLUGINS_DIR="$NEO4J_VOL/plugins"
local GDS_VERSION="2.6.7"
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The GDS version is hardcoded. Consider making this configurable via an environment variable (e.g., GDS_VERSION="${GDS_VERSION:-2.6.7}") to allow users to specify different versions without modifying the script.

Suggested change
local GDS_VERSION="2.6.7"
local GDS_VERSION="${GDS_VERSION:-2.6.7}"

Copilot uses AI. Check for mistakes.
local GDS_JAR="neo4j-graph-data-science-${GDS_VERSION}.jar"
local GDS_URL="https://github.com/neo4j/graph-data-science/releases/download/${GDS_VERSION}/${GDS_JAR}"
local GDS_SHA256="ecdad4b1050f7727af1af76579344b792b4ea8e48102955bec0e99d7cb8a46e9"
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded SHA-256 checksum is tightly coupled to the hardcoded version. If the version becomes configurable, this checksum validation will fail for other versions. Consider either documenting this limitation or implementing a version-to-checksum mapping.

Suggested change
local GDS_SHA256="ecdad4b1050f7727af1af76579344b792b4ea8e48102955bec0e99d7cb8a46e9"
# Mapping of supported GDS versions to their SHA-256 checksums.
# To add support for a new version, add a new entry below:
declare -A GDS_SHA256_MAP=(
["2.6.7"]="ecdad4b1050f7727af1af76579344b792b4ea8e48102955bec0e99d7cb8a46e9"
# Add more versions here as needed, e.g.:
# ["2.7.0"]="sha256sum_for_2.7.0"
)
local GDS_SHA256="${GDS_SHA256_MAP[$GDS_VERSION]}"
if [ -z "$GDS_SHA256" ]; then
echo -e "${RED}Error: No SHA-256 checksum known for GDS version $GDS_VERSION.${NC}"
echo "Please update the script to add the checksum for this version."
return 1
fi

Copilot uses AI. Check for mistakes.

# Try to create plugins directory if it doesn't exist
# This works on first run before Neo4j takes ownership
if [ ! -d "$PLUGINS_DIR" ]; then
mkdir -p "$PLUGINS_DIR" 2>/dev/null || true
fi

# Check if plugin already exists
if [ -f "$PLUGINS_DIR/$GDS_JAR" ]; then
echo "GDS plugin already installed."
return 0
fi

# Try to download directly to plugins directory
# This works on first run before Neo4j takes ownership
if [ -w "$PLUGINS_DIR" ] || [ ! -e "$PLUGINS_DIR" ]; then
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition [ ! -e \"$PLUGINS_DIR\" ] will be true even if the parent directory doesn't exist, leading to attempted downloads that will fail. This doesn't properly guard against the case where $NEO4J_VOL itself doesn't exist. The download will fail but the error handling may not clearly indicate the root cause.

Copilot uses AI. Check for mistakes.
echo "Downloading Neo4j GDS plugin version ${GDS_VERSION}..."
local download_status
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$PLUGINS_DIR/$GDS_JAR" "$GDS_URL"
download_status=$?
elif command -v wget >/dev/null 2>&1; then
wget -q -O "$PLUGINS_DIR/$GDS_JAR" "$GDS_URL"
download_status=$?
else
echo "Warning: Neither curl nor wget found. GDS plugin will not be installed."
return 1
fi

if [ $download_status -eq 0 ]; then
# Verify checksum
if command -v sha256sum >/dev/null 2>&1; then
local computed_hash=$(sha256sum "$PLUGINS_DIR/$GDS_JAR" | cut -d' ' -f1)
if [ "$computed_hash" = "$GDS_SHA256" ]; then
echo "GDS plugin downloaded and verified successfully."
else
echo "Warning: Checksum verification failed. Removing downloaded file."
rm -f "$PLUGINS_DIR/$GDS_JAR" 2>/dev/null
return 1
fi
else
echo "Warning: sha256sum not found. Skipping checksum verification."
echo "GDS plugin downloaded successfully (without verification)."
fi
else
echo "Warning: Failed to download GDS plugin. Some features may not work."
rm -f "$PLUGINS_DIR/$GDS_JAR" 2>/dev/null
return 1
fi
else
# Directory not writable (Neo4j has taken ownership)
# Plugin should have been installed on first run
echo "Warning: Cannot write to plugins directory. GDS plugin should have been installed on first run."
echo "If Neo4j was run without the GDS plugin, please delete the workspace and start fresh:"
echo " rm -rf '$DATA_DIR'"
return 1
fi
fi
}

function run_neo4j() {
echo Running neo4j container ... &&
podman run \
Expand All @@ -101,6 +169,7 @@ function run_neo4j() {
--publish 127.0.0.1:7474:7474 \
--publish 127.0.0.1:7687:7687 \
-e "NEO4J_AUTH=neo4j/bloodhoundcommunityedition" \
-e "NEO4J_PLUGINS=[\"graph-data-science\"]" \
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NEO4J_PLUGINS environment variable is set unconditionally, even when INSTALL_GDS=false. This will cause Neo4j to expect the GDS plugin but it won't be present, potentially causing errors. This environment variable should only be set when INSTALL_GDS=true.

Copilot uses AI. Check for mistakes.
"$NEO4J_IMAGE" \
>/dev/null
}
Expand All @@ -125,6 +194,7 @@ function run_bloodhound() {
}

run_postgres
install_gds_plugin
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The install_gds_plugin function is called before run_neo4j, but it attempts to download files to $NEO4J_VOL/plugins which may not exist yet. If Neo4j has never run, this directory won't exist and the mkdir at line 103 may fail silently due to || true. Consider ensuring the base $NEO4J_VOL directory exists before attempting plugin installation, or handle the case where the directory creation fails more explicitly.

Copilot uses AI. Check for mistakes.
run_neo4j

# Not waiting for postgres because it's faster than neo4j anyway.
Expand Down