Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3a96b7d
Fix missing \$PYTHON_MGR_CMD in --use-uv branches
edmcman May 15, 2026
1f900c5
Fix uv dep install: use uv sync and uv pip install correctly
edmcman May 15, 2026
c098f9b
Refactor pip install abstraction for uv and poetry
edmcman May 15, 2026
d3b7cc4
Fix uv service startup: swap sed order and mark project as non-package
edmcman May 15, 2026
855500f
Update installer/cape2.sh
edmcman May 15, 2026
a3e1b11
Update installer/cape2.sh
edmcman May 15, 2026
e6fcc1c
Update installer/cape2.sh
edmcman May 15, 2026
70d5bfe
Manually apply gemini suggestion
edmcman May 15, 2026
a10acad
Fix suricata install ordering and missing groups
edmcman May 15, 2026
ebd55e5
Fix uv install: create .venv in install_dependencies after user setup
edmcman May 15, 2026
1789adf
Fix IFACE_IP arg parsing: accept IP as $2 with any number of addition…
edmcman May 15, 2026
b735775
Fix uv venv creation: ensure CAPE_ROOT exists before creating venv
edmcman May 15, 2026
e177d0d
Fix USE_UV env var not selecting uv as package manager
edmcman May 15, 2026
5a89aa7
Fix LIBVIRT_DEFAULT_URI set only when installing virt-manager
edmcman May 16, 2026
273877c
fix
doomedraven May 26, 2026
3053a5b
feat(web): generic OIDC SSO via django-allauth (Okta/Azure/Auth0/Keyc…
wmetcalf Jun 1, 2026
15db749
harden OIDC adapter and okta sync (review feedback)
wmetcalf Jun 2, 2026
f2f266a
review: address Copilot feedback (security hardening + apikey cleanups)
wmetcalf Jun 4, 2026
187ead7
Ensure machine cleanup and handle unexpected errors
doomedraven Jun 7, 2026
668b276
Use task-scoped file paths; simplify stop_machine
doomedraven Jun 7, 2026
a6285c0
fix
doomedraven Jun 7, 2026
a67eb16
Update test_analysis_manager.py
doomedraven Jun 7, 2026
7089dcd
Update test_analysis_manager.py
doomedraven Jun 7, 2026
f7c7a57
Update test_analysis_manager.py
doomedraven Jun 7, 2026
65f5bc1
Update test_analysis_manager.py
doomedraven Jun 7, 2026
4eb823e
Update test_analysis_manager.py
doomedraven Jun 7, 2026
eedf8c6
Refactor demux.py for improved file handling and type hints
doomedraven Jun 8, 2026
bc0dade
Refactor find_payload_to_run to return list of executables
doomedraven Jun 8, 2026
01a0b26
Merge pull request #3066 from kevoreilly/handle_dead_vm
kevoreilly Jun 10, 2026
b2f784d
Merge pull request #3067 from kevoreilly/doomedraven-patch-1
kevoreilly Jun 10, 2026
412180b
Merge pull request #3054 from wmetcalf/feat/oidc-sso
kevoreilly Jun 10, 2026
57e1282
Merge pull request #3030 from edmcman/install-patches
kevoreilly Jun 10, 2026
0bd9d37
ci: Update requirements.txt
actions-user Jun 10, 2026
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 @@ -25,3 +25,4 @@ installer/kvm-config.sh

docs/book/src/_build
/.vs
.venv
52 changes: 50 additions & 2 deletions conf/default/web.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ enabled = no
captcha = no
2fa = no
# To enable Oauth check https://django-allauth.readthedocs.io and web/web/settings.py.
# Allow only SSO for users with specific domain. Can be allow to all if empty.
social_auth_email_domain = example.com
# Allow only SSO for users with a specific email domain. Leave blank to allow any.
social_auth_email_domain =

[registration]
enabled = no
Expand All @@ -30,6 +30,13 @@ disposable_domain_list = data/safelist/disposable_domain_list.txt

[general]
timezone = UTC
# Set to yes only when CAPE is served behind a reverse proxy (e.g. nginx) that
# terminates TLS and strips/overwrites X-Forwarded-Proto / X-Forwarded-Host from
# clients. Enables Django's SECURE_PROXY_SSL_HEADER + USE_X_FORWARDED_HOST so
# request.is_secure() and absolute URLs (incl. the OIDC redirect_uri) are correct.
# Leave no for a directly-exposed app — trusting forwarded headers would allow
# proto/host spoofing.
behind_proxy = no
# Prescan new file tasks with YARA for sample identification and custom execution
# Useful to set options, tags, timeout, etc for packers/obfuscators/cryptors
yara_recon = no
Expand Down Expand Up @@ -150,6 +157,47 @@ github = no
gitlab = no
twitter = no


# OpenID Connect SSO. Generic — works with Okta / Azure AD / Auth0 / Google
# Workspace / Keycloak or any OIDC-compliant IdP via django-allauth's
# openid_connect provider. Set enabled = yes and fill in the fields below.
[oauth_oidc]
enabled = no
# Arbitrary identifier; becomes part of the callback URL:
# /accounts/oidc/<provider_id>/login/callback/
# Must match the redirect URI registered in your IdP app.
provider_id = oidc
# Display name shown on the login button.
name = SSO
# OAuth2 client credentials from your IdP app.
client_id =
client_secret =
# IdP discovery root — the /.well-known/openid-configuration is appended
# automatically. Examples:
# Okta: https://<domain>/oauth2/default
# Azure: https://login.microsoftonline.com/<tenant>/v2.0
# Auth0: https://<domain>
# Keycloak: https://<host>/realms/<realm>
server_url =
# Group-claim -> Django role mapping. The IdP must include group names in the
# ID token under `groups_claim` (default: "groups").
# required_groups: if set, only users in at least one of these groups get a
# CAPE account provisioned. Leave blank to allow any IdP-authenticated user.
# admin_groups / superadmin_groups: membership maps to is_staff / is_superuser
# and is reconciled on every login (removal from the group demotes the user).
# Leave both blank to manage roles manually in Django.
required_groups =
admin_groups =
superadmin_groups =
groups_claim = groups
# Optional: periodic Okta account-status reconciliation (okta_user_sync
# management command, run via systemd timer). When a user with active API keys
# is no longer ACTIVE in Okta, they are deactivated locally and their keys are
# revoked. Requires an Okta admin API base URL and an SSWS token with the
# okta.users.read scope. Leave blank to disable.
admin_api_url =
admin_api_token =

[display_browser_martians]
enabled = no

Expand Down
107 changes: 45 additions & 62 deletions installer/cape2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,28 @@ TOR_SOCKET_TIMEOUT="60"
CAPE_ROOT="${CAPE_ROOT:-/opt/CAPEv2}"

USE_UV=${USE_UV:-false}
PYTHON_MGR="/etc/poetry/bin/poetry"
PYTHON_MGR_CMD="run"
PYTHON_MGR_INSTALL="install"

set_python_mgr() {
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
PYTHON_MGR="/usr/local/bin/uv"
PYTHON_MGR_CMD="run"
PYTHON_MGR_PIP="pip"
PYTHON_MGR_INSTALL_PYPROJECT="sync --no-install-project"
else
PYTHON_MGR="/etc/poetry/bin/poetry"
PYTHON_MGR_CMD="run"
PYTHON_MGR_PIP="run pip"
PYTHON_MGR_INSTALL_PYPROJECT="install"
fi
}

# if a config file is present, read it in
if [ -f "./cape-config.sh" ]; then
. ./cape-config.sh
fi

set_python_mgr

UBUNTU_VERSION=$(lsb_release -rs)
OS="$(uname -s)"
MAINTAINER="$(whoami) "_"$(hostname)"
Expand Down Expand Up @@ -666,11 +679,7 @@ function redsocks2() {
function distributed() {
echo "[+] Configure distributed configuration"
sudo apt-get install -y uwsgi uwsgi-plugin-python3 nginx 2>/dev/null
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_CMD pip install flask flask-restful flask-sqlalchemy requests"
else
sudo -u ${USER} bash -c "$PYTHON_MGR $PYTHON_MGR_CMD pip install flask flask-restful flask-sqlalchemy requests"
fi
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_PIP install flask flask-restful flask-sqlalchemy requests"

sudo cp $CAPE_ROOT/uwsgi/capedist.ini /etc/uwsgi/apps-available/cape_dist.ini
sudo ln -s /etc/uwsgi/apps-available/cape_dist.ini /etc/uwsgi/apps-enabled
Expand Down Expand Up @@ -763,6 +772,8 @@ file-store.enabled: yes
EOF

sed -i '$a include:\n - cape.yaml\n' /etc/suricata/suricata.yaml
getent group pcap || groupadd --system pcap
getent group suricata || groupadd --system suricata
usermod -aG pcap suricata
usermod -aG suricata "${USER}"
# sudo chmod -R g+w /var/log/suricata/
Expand All @@ -788,7 +799,7 @@ function install_yara_x() {
sudo -u ${USER} git clone https://github.com/VirusTotal/yara-x
cd yara-x || return
sudo -u ${USER} bash -c 'source "$HOME/.cargo/env" ; cargo install --path cli'
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_CMD pip install yara-x
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_PIP install yara-x
}

function install_yara_python() {
Expand All @@ -804,21 +815,12 @@ function install_yara_python() {
# This replaces the legacy setup.py build approach

# Install from PyPI
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR pip install yara-python \
--no-binary :all: \
--config-settings=\"--global-option=build\" \
--config-settings=\"--global-option=--enable-cuckoo\" \
--config-settings=\"--global-option=--enable-magic\" \
--config-settings=\"--global-option=--enable-profiling\""
else
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT $PYTHON_MGR_CMD pip install yara-python \
--no-binary :all: \
--config-settings="--global-option=build" \
--config-settings="--global-option=--enable-cuckoo" \
--config-settings="--global-option=--enable-magic" \
--config-settings="--global-option=--enable-profiling"
fi
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT $PYTHON_MGR_PIP install yara-python \
--no-binary :all: \
--config-settings="--global-option=build" \
--config-settings="--global-option=--enable-cuckoo" \
--config-settings="--global-option=--enable-magic" \
--config-settings="--global-option=--enable-profiling"

# Install from local source (commented out)
# sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT $PYTHON_MGR_CMD pip install /tmp/yara-python \
Expand Down Expand Up @@ -909,16 +911,7 @@ function install_libvirt() {
export_path="${temp_export_path%/*}/"
export PKG_CONFIG_PATH=$export_path

# Run build and install within the project environment
# We use sudo -u cape ... to install into the user's environment managed by poetry/uv/pip
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
# sudo -u ${USER} bash -c "export PKG_CONFIG_PATH=$export_path; cd $CAPE_ROOT && $PYTHON_MGR pip install /tmp/libvirt-python-${LIB_VERSION}"
sudo -u ${USER} bash -c "export PKG_CONFIG_PATH=$export_path; cd $CAPE_ROOT && $PYTHON_MGR pip install libvirt-python==${LIB_VERSION}"
elif [ "$PYTHON_MGR" = "/etc/poetry/bin/poetry" ]; then
sudo -u ${USER} bash -c "export PKG_CONFIG_PATH=$export_path; $PYTHON_MGR --directory $CAPE_ROOT $PYTHON_MGR_CMD pip install libvirt-python==${LIB_VERSION}"
else
sudo -u ${USER} bash -c "export PKG_CONFIG_PATH=$export_path; pip3 install libvirt-python==${LIB_VERSION}"
fi
sudo -u ${USER} bash -c "export PKG_CONFIG_PATH=$export_path; $PYTHON_MGR --directory $CAPE_ROOT $PYTHON_MGR_PIP install libvirt-python==${LIB_VERSION}"
}

function install_mongo(){
Expand Down Expand Up @@ -1061,11 +1054,7 @@ function install_capa() {
cd capa || return
git pull
git submodule update --init rules
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_CMD pip install /tmp/capa"
else
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_CMD pip install /tmp/capa
fi
sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_PIP install /tmp/capa
cd $CAPE_ROOT
if [ -d /tmp/capa ]; then
sudo rm -rf /tmp/capa
Expand Down Expand Up @@ -1367,6 +1356,9 @@ function install_CAPE() {
git clone https://github.com/kevoreilly/CAPEv2/ "$CAPE_ROOT"
fi
chown ${USER}:${USER} -R "$CAPE_ROOT"/
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
sudo -u ${USER} /usr/local/bin/uv venv "$CAPE_ROOT/.venv"
fi
#chown -R root:${USER} /usr/var/malheur/
#chmod -R =rwX,g=rwX,o=X /usr/var/malheur/
# Adapting owner permissions to the ${USER} path folder
Expand All @@ -1380,7 +1372,7 @@ function install_CAPE() {
echo "[-] pyproject.toml not found in $CAPE_ROOT"
return
fi
sudo -u ${USER} bash -c "export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring; CRYPTOGRAPHY_DONT_BUILD_RUST=1 $PYTHON_MGR pip install -r pyproject.toml"
sudo -u ${USER} bash -c "cd $CAPE_ROOT && export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring; export CRYPTOGRAPHY_DONT_BUILD_RUST=1; $PYTHON_MGR $PYTHON_MGR_INSTALL_PYPROJECT"

if [ "$DISABLE_LIBVIRT" -eq 0 ]; then
# Integrated libvirt install
Expand Down Expand Up @@ -1471,11 +1463,12 @@ function install_systemd() {
fi

if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
# Remove poetry config ExecStartPre lines BEFORE replacing poetry→uv so the
# pattern still matches (after replacement the path no longer contains /poetry)
sed -i "\|^ExecStartPre=.*/poetry .*|d" /lib/systemd/system/cape-fstab.service || true
sed -i "\|^ExecStartPre=.*/poetry .*|d" /lib/systemd/system/cape-rooter.service || true
sed -i "s|/etc/poetry/bin/poetry|$PYTHON_MGR|g" /lib/systemd/system/cape*.service
sed -i "s|/etc/poetry/bin/poetry|$PYTHON_MGR|g" /lib/systemd/system/guac*.service
# remove poetry config commands as uv does not have them or needs them
sed -i "s|^ExecStartPre=.*/poetry .*||g" /lib/systemd/system/cape-fstab.service || true
sed -i "s|^ExecStartPre=.*/poetry .*||g" /lib/systemd/system/cape-rooter.service || true
fi

systemctl daemon-reload
Expand Down Expand Up @@ -1542,13 +1535,8 @@ function install_node_exporter() {
function install_volatility3() {
echo "[+] Installing volatility3"
sudo apt-get install -y unzip
if [ "$USE_UV" = "true" ] || [ "$USE_UV" = "True" ]; then
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_CMD pip install git+https://github.com/volatilityfoundation/volatility3"
vol_path=$(sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR run python3 -c \"import volatility3.plugins;print(volatility3.__file__.replace('__init__.py', 'symbols/'))\"")
else
sudo -u ${USER} $PYTHON_MGR $PYTHON_MGR_CMD pip3 install git+https://github.com/volatilityfoundation/volatility3
vol_path=$(sudo -u ${USER} $PYTHON_MGR $PYTHON_MGR_CMD python3 -c "import volatility3.plugins;print(volatility3.__file__.replace('__init__.py', 'symbols/'))")
fi
sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_PIP install git+https://github.com/volatilityfoundation/volatility3"
vol_path=$(sudo -u ${USER} bash -c "cd $CAPE_ROOT && $PYTHON_MGR $PYTHON_MGR_CMD python3 -c \"import volatility3.plugins;print(volatility3.__file__.replace('__init__.py', 'symbols/'))\"")

if [ -z "$vol_path" ]; then
echo "[-] Could not find volatility3 path"
Expand Down Expand Up @@ -1636,7 +1624,7 @@ function install_guacamole() {
sudo usermod www-data -G ${USER}

cd $CAPE_ROOT
sudo -u ${USER} bash -c "export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring; ${poetry_path} $PYTHON_MGR_INSTALL"
sudo -u ${USER} bash -c "cd $CAPE_ROOT && export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring; $PYTHON_MGR $PYTHON_MGR_INSTALL_PYPROJECT"
cd ..

systemctl daemon-reload
Expand Down Expand Up @@ -1747,9 +1735,8 @@ case $COMMAND in
exit 0;;
esac

if [ $# -eq 3 ]; then
sandbox_version=$2
IFACE_IP=$3
if [ $# -ge 2 ] && [[ ! "$2" =~ ^-- ]]; then
IFACE_IP=$2
elif [ $# -eq 0 ]; then
echo "[-] check --help"
exit 1
Expand All @@ -1768,14 +1755,10 @@ for i in "$@"; do
DISABLE_LIBVIRT=1
elif [ "$i" == "--use-uv" ] || [ "$i" == "USE_UV=true" ] || [ "$i" == "USE_UV=True" ]; then
USE_UV="true"
PYTHON_MGR="/usr/local/bin/uv"
PYTHON_MGR_CMD="run"
PYTHON_MGR_INSTALL=""
set_python_mgr
fi
done

sandbox_version=$(echo "$sandbox_version"|tr "{A-Z}" "{a-z}")

#check if start with root
if [ "$EUID" -ne 0 ] && [[ -z "${BUILD_ENV}" ]]; then
echo 'This script must be run as root'
Expand All @@ -1788,8 +1771,8 @@ case "$COMMAND" in
install_mongo
install_CAPE
install_yara
install_systemd
install_suricata
install_systemd
install_jemalloc
if ! crontab -l | grep -q './smtp_sinkhole.sh'; then
crontab -l | { cat; echo "@reboot cd $CAPE_ROOT/utils/ && ./smtp_sinkhole.sh 2>/dev/null"; } | crontab -
Expand All @@ -1811,8 +1794,8 @@ case "$COMMAND" in
install_volatility3
install_mongo
install_yara
install_systemd
install_suricata
install_systemd
install_jemalloc
install_logrotate
install_mitmproxy
Expand All @@ -1825,7 +1808,7 @@ case "$COMMAND" in
fi
# Update FLARE CAPA rules once per day
if ! crontab -l | grep -q 'community.py -waf -cr'; then
crontab -l | { cat; echo "5 0 */1 * * cd $CAPE_ROOT/utils/ && sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_CMD python3 community.py -waf -cr && sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_CMD pip install -U flare-capa && systemctl restart cape-processor 2>/dev/null"; } | crontab -
crontab -l | { cat; echo "5 0 */1 * * cd $CAPE_ROOT/utils/ && sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_CMD python3 community.py -waf -cr && sudo -u ${USER} $PYTHON_MGR --directory $CAPE_ROOT/ $PYTHON_MGR_PIP install -U flare-capa && systemctl restart cape-processor 2>/dev/null"; } | crontab -
fi
install_librenms
if [ "$clamav_enable" -ge 1 ]; then
Expand Down
20 changes: 13 additions & 7 deletions installer/kvm-qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,18 @@ EOH
echo "[+] You should logout and login "
fi

_set_libvirt_default_uri
}

function _set_libvirt_default_uri() {
local rc_file
if [ "$SHELL" = "/bin/zsh" ] || [ "$SHELL" = "/usr/bin/zsh" ]; then
rc_file="$HOME/.zshrc"
else
rc_file="$HOME/.bashrc"
fi
grep -qxF 'export LIBVIRT_DEFAULT_URI=qemu:///system' "$rc_file" 2>/dev/null \
|| echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> "$rc_file"
}

function install_virt_manager() {
Expand Down Expand Up @@ -688,13 +700,7 @@ function install_virt_manager() {
# https://github.com/virt-manager/virt-manager/blob/main/INSTALL.md
meson setup build
meson install -C build
if [ "$SHELL" = "/bin/zsh" ] || [ "$SHELL" = "/usr/bin/zsh" ] ; then
echo "export LIBVIRT_DEFAULT_URI=qemu:///system" >> "$HOME/.zsh"
# echo "export GI_TYPELIB_PATH=/usr/local/lib/girepository-1.0:$GI_TYPELIB_PATH" >> "$HOME/.zsh"
else
echo "export LIBVIRT_DEFAULT_URI=qemu:///system" >> "$HOME/.bashrc"
# echo "export GI_TYPELIB_PATH=/usr/local/lib/girepository-1.0:$GI_TYPELIB_PATH" >> "$HOME/.bashrc"
fi
_set_libvirt_default_uri

if [ -f /usr/share/virt-manager/local/share/glib-2.0/schemas/org.virt-manager.virt-manager.gschema.xml ]; then
cp /usr/share/virt-manager/local/share/glib-2.0/schemas/org.virt-manager.virt-manager.gschema.xml /usr/share/glib-2.0/schemas/
Expand Down
Loading
Loading