Skip to content
Closed
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 .flask_history/dd13508f-e100-4aa4-9d3e-d6ae71fababc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[2025-11-08 19:38](admin admin): Url/Tool 'https://github.com/cudeso/flowintel.git' created for 'Extract disk'
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ __pycache__/
.env.local
tmp/
/logs

# Ignore config files
conf/config.py
conf/config_modules.py
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ Flowintel is an open-source platform designed to assist analysts in organizing t

## Quick start

Change the **configuration** `/conf/config.py`
Copy the **default configuration**:

run the **installation** script `./install.sh`
```
cd flowintel
cp conf/config.py.default conf/config.py
cp conf/config_module.py.default conf/config_module.py
```

Change the **configuration** in `conf/config.py`

Run the **installation** script `./install.sh`

**Start** the application with `./launch.sh -l`

Expand Down
6 changes: 1 addition & 5 deletions app/static/js/case/TaskComponent/tab-main.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {display_toast} from '/static/js/toaster.js'
const { ref } = Vue
import subtask from './subtask.js'
import TaskUrlTool from './TaskUrlTool.js'
export default {
delimiters: ['[[', ']]'],
components: {
subtask,
TaskUrlTool
subtask
},
props: {
cases_info: Object,
Expand Down Expand Up @@ -218,8 +216,6 @@ export default {
</fieldset>
</div>

<TaskUrlTool :task="task" :cases_info="cases_info" :is_template="false"></TaskUrlTool>

<div class="col" v-if="!cases_info.permission.read_only && cases_info.present_in_case || cases_info.permission.admin">
<fieldset class="analyzer-select-case">
<legend class="analyzer-select-case"><i class="fa-solid fa-temperature-three-quarters"></i> Status</legend>
Expand Down
26 changes: 25 additions & 1 deletion app/static/js/case/case_tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import tabMain from './TaskComponent/tab-main.js'
import tabNote from './TaskComponent/tab-note.js'
import tabFile from './TaskComponent/tab-file.js'
import tabInfo from './TaskComponent/tab-info.js'
import TaskUrlTool from './TaskComponent/TaskUrlTool.js'
import caseconnectors from './CaseConnectors.js'
import {truncateText, getTextColor, mapIcon} from '/static/js/utils.js'
const { ref, nextTick} = Vue
Expand All @@ -24,6 +25,7 @@ export default {
tabNote,
tabFile,
tabInfo,
TaskUrlTool,
caseconnectors
},
setup(props) {
Expand Down Expand Up @@ -183,6 +185,7 @@ export default {
document.getElementById("tab-task-notes-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-files-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-connectors-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-urls-tools-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-info-"+props.task.id).classList.remove("active")
}
}else if(tab_name == 'notes'){
Expand All @@ -192,6 +195,7 @@ export default {
document.getElementById("tab-task-main-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-files-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-connectors-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-urls-tools-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-info-"+props.task.id).classList.remove("active")
}
await nextTick()
Expand All @@ -205,6 +209,7 @@ export default {
document.getElementById("tab-task-main-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-notes-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-connectors-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-urls-tools-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-info-"+props.task.id).classList.remove("active")
}
}else if(tab_name == 'connectors'){
Expand All @@ -216,6 +221,18 @@ export default {
document.getElementById("tab-task-main-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-notes-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-files-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-urls-tools-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-info-"+props.task.id).classList.remove("active")
}
}
else if(tab_name == 'urls_tools'){
selected_tab.value = 'urls_tools'
if ( !document.getElementById("tab-task-urls-tools-"+props.task.id).classList.contains("active") ){
document.getElementById("tab-task-urls-tools-"+props.task.id).classList.add("active")
document.getElementById("tab-task-main-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-notes-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-files-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-connectors-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-info-"+props.task.id).classList.remove("active")
}
}
Expand All @@ -227,7 +244,7 @@ export default {
document.getElementById("tab-task-notes-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-files-"+props.task.id).classList.remove("active")
document.getElementById("tab-task-connectors-"+props.task.id).classList.remove("active")

document.getElementById("tab-task-urls-tools-"+props.task.id).classList.remove("active")
}
}

Expand Down Expand Up @@ -455,6 +472,9 @@ export default {
<li class="nav-item">
<button class="nav-link" :id="'tab-task-files-'+task.id" @click="select_tab_task('files')">Files</button>
</li>
<li class="nav-item">
<button class="nav-link" :id="'tab-task-urls-tools-'+task.id" @click="select_tab_task('urls_tools')">Urls/Tools</button>
</li>
<li class="nav-item">
<button class="nav-link" :id="'tab-task-info-'+task.id" @click="select_tab_task('info')">Info</button>
</li>
Expand Down Expand Up @@ -496,6 +516,10 @@ export default {
<tabFile :cases_info="cases_info" :task="task"></tabFile>
</template>

<template v-else-if="selected_tab == 'urls_tools'">
<TaskUrlTool :task="task" :cases_info="cases_info" :is_template="false"></TaskUrlTool>
</template>

<template v-else-if="selected_tab == 'info'">
<tabInfo :task="task" :cases_info="cases_info" :open_closed="open_closed"></tabInfo>
</template>
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions conf/config_module.py.default
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ORIGIN_URL = 'localhost:7006' # Used for connector, to know what's the origin of the data

# SMTP Parameters
SMTP_SERVER = ""
SMTP_PORT = 587 # Port TLS
SENDER_EMAIL = ""
SENDER_PASSOWRD = ""

# Matrix bot
MATRIX_SERVER = ""
MATRIX_USER = ""
MATRIX_PASSWORD = ""
MATRIX_ROOM_ID = ""
186 changes: 186 additions & 0 deletions install.safe.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

info(){ printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
warn(){ printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
err(){ printf '\033[1;31m[ERROR]\033[0m %s\n' "$*"; exit 1; }

# Install variables
VENV_DIR="env"
NODE_VERSION="${NODE_VERSION:-20.19.2}"
NVM_VERSION="v0.40.3"
PANDOC_RELEASE="3.7"
PANDOC_VERSION="3.7-1"
# Get arch safely
if command -v dpkg >/dev/null 2>&1; then
ARCH="$(dpkg --print-architecture)"
else
ARCH="$(uname -m)"
fi
PANDOC_DEB_URL="https://github.com/jgm/pandoc/releases/download/${PANDOC_RELEASE}/pandoc-${PANDOC_VERSION}-${ARCH}.deb"

# require_cmd: check command exists
require_cmd(){ command -v "$1" >/dev/null 2>&1 || err "Required command '$1' not found. Please install it and re-run."; }

# detect_pkg_mgr: detect apt or dnf
detect_pkg_mgr(){
if command -v apt-get >/dev/null 2>&1; then echo "apt"; return; fi
if command -v dnf >/dev/null 2>&1; then echo "dnf"; return; fi
err "Unsupported package manager (need apt-get or dnf).";
}
PKG_MGR=$(detect_pkg_mgr)
info "Detected package manager: ${PKG_MGR}"

# do we have sudo? (if not root)
if [ "$EUID" -ne 0 ]; then
if ! command -v sudo >/dev/null 2>&1; then
err "Script requires sudo for system package installs. Install sudo or run as root."
fi
fi

# system packages
if [ "$PKG_MGR" = "apt" ]; then
info "Updating apt cache and installing system packages..."
sudo apt-get update -y
sudo apt-get install -y python3-venv git screen libolm-dev librsvg2-bin wget texlive texlive-xetex texlive-fonts-extra
elif [ "$PKG_MGR" = "dnf" ]; then
info "Installing system packages via dnf..."
sudo dnf install -y epel-release || warn "epel-release may already be installed"
sudo crb enable || warn "crb may already be enabled"
sudo dnf --refresh -y install pandoc python3 git screen libolm librsvg2 wget valkey || warn "dnf install reported warnings"
fi

# pandoc
if [ "$PKG_MGR" = "apt" ]; then
if ! command -v pandoc >/dev/null 2>&1; then
info "Get pandoc ${PANDOC_VERSION}"
tmp="$(mktemp)"
curl -fsSL -o "$tmp" "$PANDOC_DEB_URL" || { rm -f "$tmp"; err "Failed to download pandoc"; }
info "Install pandoc (.deb)"
sudo dpkg -i "$tmp" || sudo apt-get install -f -y
rm -f "$tmp"
else
info "pandoc already installed; skipping"
fi
fi

# nvm + node
if [ -s "$HOME/.nvm/nvm.sh" ]; then
info "nvm already installed"
else
info "Installing nvm ${NVM_VERSION}"
nvm_installer_tmp="$(mktemp)"
curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" -o "$nvm_installer_tmp" || { rm -f "$nvm_installer_tmp"; err "Failed to download nvm installer"; }
bash "$nvm_installer_tmp" || { rm -f "$nvm_installer_tmp"; err "nvm installer failed"; }
rm -f "$nvm_installer_tmp"
fi

# Load nvm
export NVM_DIR="$HOME/.nvm"
if [ -s "$NVM_DIR/nvm.sh" ]; then
. "$NVM_DIR/nvm.sh"
else
warn "nvm not available in this shell"
fi

# install/use node
if command -v nvm >/dev/null 2>&1; then
if ! nvm ls "$NODE_VERSION" >/dev/null 2>&1; then
info "Installing node ${NODE_VERSION} via nvm"
nvm install "$NODE_VERSION"
fi
nvm use "$NODE_VERSION" >/dev/null 2>&1 || true
else
warn "nvm not found; skipping node/npm installs. Ensure it's >= ${NODE_VERSION}."
fi

export PATH="$HOME/node_modules/.bin:$PATH"
info "Installing mermaid tools locally (mermaid-filter and mermaid-cli)"
# use npm only if available
if command -v npm >/dev/null 2>&1; then
npm install --no-audit --no-fund --prefix "$HOME" mermaid-filter @mermaid-js/mermaid-cli || warn "npm install had warnings"
else
warn "npm not available; skipping mermaid installs"
fi

# Update ~/.bashrc
add_line_if_missing(){
local line="$1" file="$2"
touch "$file"
grep -qxF "$line" "$file" || printf '%s\n' "$line" >> "$file"
}
add_line_if_missing "export NVM_DIR=\"$NVM_DIR\"" "$HOME/.bashrc"
add_line_if_missing '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm' "$HOME/.bashrc"
add_line_if_missing 'export PATH="$PATH:$HOME/node_modules/.bin"' "$HOME/.bashrc"

# mmdc wrapper
MM_BIN_DIR="$HOME/node_modules/.bin"
mkdir -p "$MM_BIN_DIR"
MMDC_ORIG="$MM_BIN_DIR/mmdc.orig"
MMDC_BIN="$MM_BIN_DIR/mmdc"

# move original if needed
if [ -x "$MM_BIN_DIR/mmdc" ] && [ ! -x "$MMDC_ORIG" ]; then
mv "$MM_BIN_DIR/mmdc" "$MMDC_ORIG" || warn "Failed to move existing mmdc to mmdc.orig"
fi

cat > "$MM_BIN_DIR/puppeteer.json" <<'JSON'
{
"args": [
"--no-sandbox"
]
}
JSON

cat > "$MMDC_BIN" <<'SH'
#!/usr/bin/env bash
HERE="$(dirname "$0")"
ORIG="$HERE/mmdc.orig"
if [ -x "$ORIG" ]; then
"$ORIG" -p "$HERE/puppeteer.json" "$@"
else
echo "mmdc original binary not found; ensure @mermaid-js/mermaid-cli is installed" >&2
exit 1
fi
SH
chmod +x "$MMDC_BIN" || warn "chmod on mmdc wrapper failed"

# Python venv and requirements
if [ ! -d "$VENV_DIR" ]; then
info "Creating python venv at $VENV_DIR"
python3 -m venv "$VENV_DIR"
else
info "Using existing python venv at $VENV_DIR"
fi
# Activate venv for the rest of the script
# shellcheck disable=SC1091
. "$VENV_DIR/bin/activate"

python -m pip install --upgrade pip setuptools wheel
if [ -f "requirements.txt" ]; then
python -m pip install -r requirements.txt
else
warn "requirements.txt not found; skipping pip install"
fi

# init submodules
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git submodule update --init --recursive
else
warn "Not in a git repo; skipping submodule init"
fi

# make launch script executable and run initial setup
if [ -f ./launch.sh ]; then
chmod +x ./launch.sh || warn "chmod launch.sh failed"
if ./launch.sh -i; then
info "Ran ./launch.sh -i successfully"
else
warn "./launch.sh -i returned non-zero exit code"
fi
else
warn "launch.sh not found; skipping"
fi

info "install.safe.sh completed successfully"
13 changes: 11 additions & 2 deletions launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ isscripted_misp_mod=`screen -ls | egrep '[0-9]+.misp_mod_flowintel' | cut -d. -f

history_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

# Directory of the python virtualenv to use; can be overridden by env var
VENV_DIR="${VENV_DIR:-env}"

function prepare_app_run {
# This function is to avoid having problem with the env for test
source env/bin/activate
mkdir -p logs # pour les fichiers de log
# Activate the configured virtualenv if present
if [ -f "$VENV_DIR/bin/activate" ]; then
# shellcheck source=/dev/null
source "$VENV_DIR/bin/activate"
else
echo "[WARN] Virtualenv '$VENV_DIR' not found; continuing without activation" >&2
fi
mkdir -p logs # Directory for log files
}

function killscript {
Expand Down