Merge branch 'develop' of github.com:MISP/MISP into develop

pull/8618/head
Christian Studer 2022-09-26 14:14:40 +02:00
commit 8a1e7c0164
560 changed files with 119636 additions and 59894 deletions

View File

@ -30,10 +30,6 @@ jobs:
with:
submodules: 'recursive'
# Stop mysql
- name: Shutdown Ubuntu MySQL (SUDO)
run: sudo service mysql stop
# Run mariadb
- uses: getong/mariadb-action@v1.1
with:
@ -61,21 +57,22 @@ jobs:
echo "USER=`id -u -n`" >> $GITHUB_ENV
echo "HOST=localhost" >> $GITHUB_ENV
# Runs a set of commands using the runners shell
- name: Install deps
- name: Install system deps
env:
php_version: ${{ matrix.php }}
run: |
sudo apt-get -y update
# Repo is missing for unknown reason
LC_ALL=C.UTF-8 sudo apt-add-repository ppa:ondrej/php -y
if [[ $php_version == "7.2" ]]; then
# hotfix due to: https://bugs.php.net/bug.php?id=81640 TODO: remove after libpcre2-8-0:10.36 gets to stable channel
sudo apt --fix-broken install
fi
sudo apt-get -y install curl python3 python3-zmq python3-requests python3-pip python3-nose python3-redis python3-lxml apache2 libapache2-mod-php$php_version
sudo pip3 install virtualenv # virtualenv must be instaled from pip and not from ubuntu packages
curl -sSL https://install.python-poetry.org | python -
sudo apt-get -y update
# Repo is missing for unknown reason
LC_ALL=C.UTF-8 sudo apt-add-repository ppa:ondrej/php -y
if [[ $php_version == "7.2" ]]; then
# hotfix due to: https://bugs.php.net/bug.php?id=81640 TODO: remove after libpcre2-8-0:10.36 gets to stable channel
sudo apt-get --fix-broken install
fi
sudo apt-get -y install curl python3 python3-pip python3-virtualenv apache2 libapache2-mod-php$php_version
# Runs a set of commands using the runners shell
- name: Install deps
run: |
sudo chown $USER:www-data $HOME/.composer
pushd app
sudo -H -u $USER php composer.phar install --no-progress
@ -149,32 +146,33 @@ jobs:
- name: Configure MISP
run: |
sudo -E su $USER -c 'app/Console/cake userInit -q | sudo tee ./key.txt'
sudo -u $USER app/Console/cake userInit -q | sudo tee ./key.txt
echo "AUTH=`cat key.txt`" >> $GITHUB_ENV
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.autoRegenerate" 0'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.timeout" 600'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.cookieTimeout" 3600'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.host_org_id" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.email" "info@admin.test"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.disable_emailing" false'
sudo -E su $USER -c 'app/Console/cake Admin setSetting --force "debug" true'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.CustomAuth_disable_logout" false'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_host" "127.0.0.1"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_port" 6379'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_database" 13'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_password" ""'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.email" "info@admin.test"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.homedir" "`pwd`/.gnupg"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.password" "travistest"'
sudo -u $USER app/Console/cake Admin setSetting "Session.autoRegenerate" 0
sudo -u $USER app/Console/cake Admin setSetting "Session.timeout" 600
sudo -u $USER app/Console/cake Admin setSetting "Session.cookieTimeout" 3600
sudo -u $USER app/Console/cake Admin setSetting "MISP.host_org_id" 1
sudo -u $USER app/Console/cake Admin setSetting "MISP.email" "info@admin.test"
sudo -u $USER app/Console/cake Admin setSetting "MISP.disable_emailing" false
sudo -u $USER app/Console/cake Admin setSetting --force "debug" true
sudo -u $USER app/Console/cake Admin setSetting "Plugin.CustomAuth_disable_logout" false
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_host" "127.0.0.1"
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_port" 6379
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_database" 13
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_password" ""
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.email" "info@admin.test"
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.homedir" "`pwd`/.gnupg"
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.password" "travistest"
sudo -u $USER app/Console/cake Admin setSetting "MISP.download_gpg_from_homedir" 1
- name: Configure ZMQ
run: |
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_host" "127.0.0.1"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_port" 6379'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_database" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_password" ""'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_enable" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_audit_notifications_enable" 1'
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_host" "127.0.0.1"
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_port" 6379
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_database" 1
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_password" ""
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_enable" 1
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_audit_notifications_enable" 1
- name: Update Galaxies
run: sudo -E su $USER -c 'app/Console/cake Admin updateGalaxies'
@ -209,16 +207,7 @@ jobs:
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.python_bin" "$GITHUB_WORKSPACE/venv/bin/python"'
. ./venv/bin/activate
export PYTHONPATH=$PYTHONPATH:./app/files/scripts
pushd ./app/files/scripts/cti-python-stix2
pip install .
popd
pushd ./app/files/scripts/python-stix
pip install .
popd
pushd PyMISP
pip install .[fileobjects,email]
popd
pip install zmq redis plyara
pip install ./PyMISP[fileobjects,email] ./app/files/scripts/python-stix ./app/files/scripts/cti-python-stix2 pyzmq redis plyara
deactivate
- name: Test if apache is working
@ -242,31 +231,32 @@ jobs:
- name: Run PHP tests
run: |
./app/Vendor/bin/parallel-lint --exclude app/Lib/cakephp/ --exclude app/Vendor/ --exclude app/Lib/random_compat/ -e php,ctp app/
./app/Vendor/bin/phpunit app/Test/
sudo -u www-data ./app/Vendor/bin/phpunit app/Test/
- name: Run tests
run: |
export PATH=$HOME/.local/env:$PATH # enable poetry binary
pushd tests
./curl_tests_GH.sh $AUTH $HOST
popd
sudo chmod -R g+ws `pwd`/app/tmp/logs
. ./venv/bin/activate
pushd PyMISP
poetry install -E fileobjects -E openioc -E virustotal -E docs -E pdfexport -E email
poetry run python tests/testlive_comprehensive.py
poetry add lxml
poetry run python ../tests/testlive_security.py -v
poetry run python ../tests/testlive_sync.py
poetry run python ../tests/testlive_comprehensive_local.py -v
poetry run python tests/test_mispevent.py
python tests/testlive_comprehensive.py
popd
python tests/testlive_security.py -v
python tests/testlive_sync.py
python tests/testlive_comprehensive_local.py -v
pushd PyMISP
python tests/test_mispevent.py
popd
cp PyMISP/tests/keys.py PyMISP/examples/events/
pushd PyMISP/examples/events/
poetry run python ./create_massive_dummy_events.py -l 5 -a 30
python ./create_massive_dummy_events.py -l 5 -a 30
popd
python3 tools/misp-feed/validate.py
python tools/misp-feed/validate.py
deactivate
- name: Logs
if: ${{ always() }}

9
.gitignore vendored
View File

@ -35,6 +35,8 @@ tools/mkdocs
/app/tmp/cache/misp_feed*
/app/files/*
/app/tmp/cache/feeds/*.cache
app/Lib/EventWarning/Custom/*
!app/Lib/EventWarning/Custom/empty
!/app/files/feed-metadata
!/app/files/empty
!/app/files/scripts/
@ -48,6 +50,8 @@ tools/mkdocs
!/app/files/misp-objects/*
!/app/files/misp-decaying-models
!/app/files/misp-decaying-models/*
!/app/files/misp-workflow-blueprints
!/app/files/misp-workflow-blueprints/*
/app/files/scripts/*.pyc
/app/files/scripts/*.py~
/app/files/scripts/__pycache__
@ -105,3 +109,8 @@ vagrant/.vagrant/
vagrant/*.log
/app/Lib/Dashboard/Custom/*
!/app/Lib/Dashboard/Custom/empty
/app/View/Emails/html/Custom/*
!/app/View/Emails/html/Custom/empty
/app/View/Emails/text/Custom/*
!/app/View/Emails/text/Custom/empty

3
.gitmodules vendored
View File

@ -48,3 +48,6 @@
[submodule "app/files/scripts/python-maec"]
path = app/files/scripts/python-maec
url = https://github.com/MAECProject/python-maec
[submodule "app/files/misp-workflow-blueprints"]
path = app/files/misp-workflow-blueprints
url = https://github.com/MISP/misp-workflow-blueprints

View File

@ -22,7 +22,7 @@
# 20210406: Ubuntu 21.04 tested and working. -- sCl
# 20210406: Ubuntu 20.04.2 tested and working. -- sCl
# 20210406: Ubuntu 18.04.5 tested and working. -- sCl
# 20210331: Kali Linux 2021.1 tested and working. -- sCl
# 20220303: Kali Linux 2022.1 tested and working. -- sCl
#
#
#-------------------------------------------------------------------------------------------------|
@ -42,7 +42,7 @@
#
# To install MISP on Kali copy paste the following to your shell:
# # wget --no-cache -O /tmp/misp-kali.sh https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh && bash /tmp/misp-kali.sh
# NO other version then 2020.x supported, kthxbai.
# NO other version then 2022.x supported, kthxbai.
# /!\ Please read the installer script before randomly doing the above.
# The script is tested on a plain vanilla Kali Linux Boot CD and installs quite a few dependencies.
#
@ -798,6 +798,16 @@ kaliUpgrade () {
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y
}
# Kali 2022.x has only php81
installDepsKaliPhp74 () {
sudo apt -y install lsb-release apt-transport-https ca-certificates
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ bullseye main" | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update
wget http://ftp.us.debian.org/debian/pool/main/libf/libffi/libffi7_3.3-6_amd64.deb
sudo dpkg -i libffi7_3.3-6_amd64.deb
}
# Disables sleep
disableSleep () {
debug "Disabling sleep etc if run from a Laptop as the install might take some time…" > /dev/tty
@ -906,7 +916,7 @@ installDeps () {
[[ -n $KALI ]] || [[ -n $UNATTENDED ]] && sudo DEBIAN_FRONTEND=noninteractive apt install -qy postfix || sudo apt install -qy postfix
sudo apt install -qy \
curl gcc git gnupg-agent make openssl redis-server neovim unzip zip libyara-dev python3-yara python3-redis python3-zmq sqlite3 \
curl gcc git gnupg-agent make openssl redis-server neovim unzip zip libyara-dev python3-yara python3-redis python3-zmq sqlite3 python3-virtualenv \
mariadb-client \
mariadb-server \
apache2 apache2-doc apache2-utils \
@ -916,74 +926,6 @@ installDeps () {
installRNG
}
# On Kali, the redis start-up script is broken. This tries to fix it.
fixRedis () {
# As of 20190124 redis-server init.d scripts are broken and need to be replaced
sudo mv /etc/init.d/redis-server /etc/init.d/redis-server_`date +%Y%m%d`
echo '#! /bin/sh
### BEGIN INIT INFO
# Provides: redis-server
# Required-Start: $syslog
# Required-Stop: $syslog
# Should-Start: $local_fs
# Should-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: redis-server - Persistent key-value db
# Description: redis-server - Persistent key-value db
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/redis-server
DAEMON_ARGS=/etc/redis/redis.conf
NAME=redis-server
DESC=redis-server
PIDFILE=/var/run/redis.pid
test -x $DAEMON || exit 0
test -x $DAEMONBOOTSTRAP || exit 0
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
touch $PIDFILE
chown redis:redis $PIDFILE
if start-stop-daemon --start --quiet --umask 007 --pidfile $PIDFILE --chuid redis:redis --exec $DAEMON -- $DAEMON_ARGS
then
echo "$NAME."
else
echo "failed"
fi
;;
stop)
echo -n "Stopping $DESC: "
if start-stop-daemon --stop --retry 10 --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
then
echo "$NAME."
else
echo "failed"
fi
rm -f $PIDFILE
;;
restart|force-reload)
${0} stop
${0} start
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0' | sudo tee /etc/init.d/redis-server
sudo chmod 755 /etc/init.d/redis-server
sudo /etc/init.d/redis-server start
}
# generate MISP apache conf
genApacheConf () {
echo "<VirtualHost _default_:80>
@ -1041,6 +983,11 @@ composer () {
${SUDO_WWW} sh -c "cd ${PATH_TO_MISP}/app ; php composer.phar install --no-dev"
}
# Legacy composer function
composer74 () {
sudo mkdir -p /var/www/.composer ; sudo chown ${WWW_USER}:${WWW_USER} /var/www/.composer
${SUDO_WWW} sh -c "cd ${PATH_TO_MISP}/app ; php7.4 composer.phar install --no-dev"
}
# TODO: FIX somehow the alias of the function does not work
# Composer on php 7.0 does not need any special treatment the provided phar works well
@ -1194,7 +1141,7 @@ checkSudoKeeper () {
installCoreDeps () {
debug "Installing core dependencies"
# Install the dependencies: (some might already be installed)
sudo apt-get install curl gcc git gpg-agent make python python3 openssl redis-server sudo vim zip unzip virtualenv libfuzzy-dev sqlite3 moreutils -qy
sudo apt-get install curl gcc git gpg-agent make python3 openssl redis-server sudo vim zip unzip virtualenv libfuzzy-dev sqlite3 moreutils -qy
# Install MariaDB (a MySQL fork/alternative)
sudo apt-get install mariadb-client mariadb-server -qy
@ -1223,7 +1170,7 @@ installDepsPhp74 () {
libapache2-mod-php7.4 \
php7.4 php7.4-cli \
php7.4-dev \
php7.4-json php7.4-xml php7.4-mysql php7.4-opcache php7.4-readline php7.4-mbstring php7.4-zip \
php7.4-json php7.4-xml php7.4-mysql php7.4-opcache php7.4-readline php7.4-mbstring php7.4-zip php7.4-curl \
php7.4-redis php7.4-gnupg \
php7.4-intl php7.4-bcmath \
php7.4-gd
@ -3218,6 +3165,9 @@ installMISPonKali () {
# Set Base URL - functionLocation('generic/supportFunctions.md')
setBaseURL
# Install PHP 7.4 (only php8.1 is available on latest Kali) - functionLocation('supportFunctions.md')
installDepsKaliPhp74
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
installDepsPhp74
@ -3249,9 +3199,6 @@ installMISPonKali () {
debug "Restarting mysql.service"
sudo systemctl restart mysql.service
debug "Fixing redis rc script on Kali"
fixRedis
debug "git clone, submodule update everything"
sudo mkdir ${PATH_TO_MISP}
sudo chown ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
@ -3265,17 +3212,11 @@ installMISPonKali () {
# Make git ignore filesystem permission differences for submodules
${SUDO_WWW} git submodule foreach --recursive git config core.filemode false
cd ${PATH_TO_MISP}/app/files/scripts
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/CybOXProject/python-cybox.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/STIXProject/python-stix.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/CybOXProject/mixbox.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/MAECProject/python-maec.git; done
sudo mkdir /var/www/.cache/
MISP_USER_HOME=$(sudo -Hiu $MISP_USER env | grep HOME |cut -f 2 -d=)
sudo mkdir $MISP_USER_HOME/.cache
sudo chown $MISP_USER:$MISP_USER $MISP_USER_HOME/.cache
MISP_USER_HOME=$(sudo -Hiu ${MISP_USER} env | grep HOME |cut -f 2 -d=)
sudo mkdir ${MISP_USER_HOME}/.cache
sudo chown ${MISP_USER}:${MISP_USER} ${MISP_USER_HOME}/.cache
sudo chown ${WWW_USER}:${WWW_USER} /var/www/.cache
## Not really needed...
@ -3298,14 +3239,13 @@ installMISPonKali () {
cd ${PATH_TO_MISP}/app/files/scripts/python-stix
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
debug "Install maec"
debug "Installing maec"
cd ${PATH_TO_MISP}/app/files/scripts/python-maec
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
# install STIX2.0 library to support STIX 2.0 export
debug "Installing cti-python-stix2"
# install STIX2.0 library to support STIX 2.0 export:
cd ${PATH_TO_MISP}/cti-python-stix2
# Install misp-stix
debug "Installing misp-stix"
cd ${PATH_TO_MISP}/app/files/scripts/misp-stix
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
debug "Installing mixbox"
@ -3333,7 +3273,7 @@ installMISPonKali () {
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install zmq
debug "Installing cake"
composer
composer74
${SUDO_WWW} cp -fa ${PATH_TO_MISP}/INSTALL/setup/config.php ${PATH_TO_MISP}/app/Plugin/CakeResque/Config/config.php
@ -3346,27 +3286,27 @@ installMISPonKali () {
debug "Setting up database"
if [[ ! -e /var/lib/mysql/misp/users.ibd ]]; then
# Kill the anonymous users
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'localhost'"
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'localhost'"
# Because our hostname varies we'll use some Bash magic here.
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'$(hostname)'"
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'$(hostname)'"
# Kill off the demo database
sudo mysql -h $DBHOST -e "DROP DATABASE IF EXISTS test"
sudo mysql -h ${DBHOST} -e "DROP DATABASE IF EXISTS test"
# No root remote logins
sudo mysql -h $DBHOST -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
sudo mysql -h ${DBHOST} -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
# Make sure that NOBODY can access the server without a password
sudo mysqladmin -h $DBHOST -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
sudo mysqladmin -h ${DBHOST} -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
# Make our changes take effect
sudo mysql -h $DBHOST -e "FLUSH PRIVILEGES"
sudo mysql -h ${DBHOST} -e "FLUSH PRIVILEGES"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "CREATE DATABASE $DBNAME;"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT USAGE ON *.* TO $DBUSER_MISP@localhost IDENTIFIED BY '$DBPASSWORD_MISP';"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT ALL PRIVILEGES ON $DBNAME.* TO '$DBUSER_MISP'@'localhost';"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "FLUSH PRIVILEGES;"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "CREATE DATABASE ${DBNAME};"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT USAGE ON *.* TO ${DBUSER_MISP}@localhost IDENTIFIED BY '${DBPASSWORD_MISP}';"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT ALL PRIVILEGES ON ${DBNAME}.* TO '${DBUSER_MISP}'@'localhost';"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "FLUSH PRIVILEGES;"
enableServices
debug "Populating database"
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u $DBUSER_MISP -p$DBPASSWORD_MISP $DBNAME
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u ${DBUSER_MISP} -p${DBPASSWORD_MISP} ${DBNAME}
echo "<?php
class DATABASE_CONFIG {
@ -3406,7 +3346,7 @@ installMISPonKali () {
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
do
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" $PHP_INI
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" ${PHP_INI}
done
debug "Restarting Apache2"
@ -3653,11 +3593,12 @@ x86_64-debian-buster
x86_64-ubuntu-bionic
x86_64-ubuntu-focal
x86_64-ubuntu-hirsute
x86_64-kali-2020.4
x86_64-kali-2021.1
x86_64-kali-2021.2
x86_64-kali-2021.3
x86_64-ubuntu-jammy
x86_64-kali-2021.4
x86_64-kali-2022.1
x86_64-kali-2022.2
x86_64-kali-2022.3
x86_64-kali-2022.4
armv6l-raspbian-stretch
armv7l-raspbian-stretch
armv7l-raspbian-buster
@ -3700,6 +3641,12 @@ if [[ "${FLAVOUR}" == "ubuntu" ]]; then
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
installSupported PHP="7.4" && exit || exit
fi
if [[ "${RELEASE}" == "22.04" ]]; then
echo "Install on Ubuntu 22.04 LTS fully supported."
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
upgradeToPHP74
installSupported PHP="7.4" && exit || exit
fi
if [[ "${RELEASE}" == "18.10" ]]; then
echo "Install on Ubuntu 18.10 partially supported, bye."
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"

View File

@ -1,5 +1,5 @@
; Generated by RHash v1.3.9 on 2021-12-25 at 14:54.47
; Generated by RHash v1.4.2 on 2022-05-23 at 12:45.34
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
;
; 161158 14:54.46 2021-12-25 INSTALL.sh
INSTALL.sh A1EEC205071442B2C9B145E7B5A054FF24728EF4 DAF55446860994E3AE8589FB6F8EE33B015DBC454ADFC8D7F3E0B2F28AA8BBBD 4F6F43B200D0F9B35C53186B4AD26A1CCFEBB6FC961B19369E3D13A053C046340B6BF1673D14793B0FE7233ECA86993D D38C7FAF648C60DCEA4D9C64AB5EB4AE262B3F1625106670DC837D1AEC9AB302BCB21811CEE5B14845D841BCDCBF420FA800BDCDCA44B5F58196667022BBD94D
; 160126 12:45.34 2022-05-23 INSTALL.sh
INSTALL.sh 4296D40B11B3002DF3FDFD69A508ED5ECACB8C13 D32E5A4B0F37F4C937CD4F85927E998D917BCBE89E4E0E864FFD7EA09E29ADEF BD093D8018C351E3D3722646E269C4B60E6DA19F42150338CE6FD72FEE293B8B89AA69D48A84B19D3EFDDAE25EC9E646 ECACC3071E130058C3DDECC86E1CBF27DD4F11389D10F43B14293B1915F7A24F02D0DA51E299706A38C00F2D2A7505B0FE46E33B705E53594383CE65461F2B08

View File

@ -1 +1 @@
a1eec205071442b2c9b145e7b5a054ff24728ef4 INSTALL.sh
4296d40b11b3002df3fdfd69a508ed5ecacb8c13 INSTALL.sh

View File

@ -1 +1 @@
daf55446860994e3ae8589fb6f8ee33b015dbc454adfc8d7f3e0b2f28aa8bbbd INSTALL.sh
d32e5a4b0f37f4c937cd4f85927e998d917bcbe89e4e0e864ffd7ea09e29adef INSTALL.sh

View File

@ -1 +1 @@
4f6f43b200d0f9b35c53186b4ad26a1ccfebb6fc961b19369e3d13a053c046340b6bf1673d14793b0fe7233eca86993d INSTALL.sh
bd093d8018c351e3d3722646e269c4b60e6da19f42150338ce6fd72fee293b8b89aa69d48a84b19d3efddae25ec9e646 INSTALL.sh

View File

@ -1 +1 @@
d38c7faf648c60dcea4d9c64ab5eb4ae262b3f1625106670dc837d1aec9ab302bcb21811cee5b14845d841bcdcbf420fa800bdcdca44b5f58196667022bbd94d INSTALL.sh
ecacc3071e130058c3ddecc86e1cbf27dd4f11389d10f43b14293b1915f7a24f02d0da51e299706a38c00f2d2a7505b0fe46e33b705e53594383ce65461f2b08 INSTALL.sh

View File

@ -22,7 +22,7 @@
# 20210406: Ubuntu 21.04 tested and working. -- sCl
# 20210406: Ubuntu 20.04.2 tested and working. -- sCl
# 20210406: Ubuntu 18.04.5 tested and working. -- sCl
# 20210331: Kali Linux 2021.1 tested and working. -- sCl
# 20220303: Kali Linux 2022.1 tested and working. -- sCl
#
#
#-------------------------------------------------------------------------------------------------|
@ -42,7 +42,7 @@
#
# To install MISP on Kali copy paste the following to your shell:
# # wget --no-cache -O /tmp/misp-kali.sh https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh && bash /tmp/misp-kali.sh
# NO other version then 2020.x supported, kthxbai.
# NO other version then 2022.x supported, kthxbai.
# /!\ Please read the installer script before randomly doing the above.
# The script is tested on a plain vanilla Kali Linux Boot CD and installs quite a few dependencies.
#
@ -421,6 +421,9 @@ installMISPonKali () {
# Set Base URL - functionLocation('generic/supportFunctions.md')
setBaseURL
# Install PHP 7.4 (only php8.1 is available on latest Kali) - functionLocation('supportFunctions.md')
installDepsKaliPhp74
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
installDepsPhp74
@ -452,9 +455,6 @@ installMISPonKali () {
debug "Restarting mysql.service"
sudo systemctl restart mysql.service
debug "Fixing redis rc script on Kali"
fixRedis
debug "git clone, submodule update everything"
sudo mkdir ${PATH_TO_MISP}
sudo chown ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
@ -468,17 +468,11 @@ installMISPonKali () {
# Make git ignore filesystem permission differences for submodules
${SUDO_WWW} git submodule foreach --recursive git config core.filemode false
cd ${PATH_TO_MISP}/app/files/scripts
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/CybOXProject/python-cybox.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/STIXProject/python-stix.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/CybOXProject/mixbox.git; done
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/MAECProject/python-maec.git; done
sudo mkdir /var/www/.cache/
MISP_USER_HOME=$(sudo -Hiu $MISP_USER env | grep HOME |cut -f 2 -d=)
sudo mkdir $MISP_USER_HOME/.cache
sudo chown $MISP_USER:$MISP_USER $MISP_USER_HOME/.cache
MISP_USER_HOME=$(sudo -Hiu ${MISP_USER} env | grep HOME |cut -f 2 -d=)
sudo mkdir ${MISP_USER_HOME}/.cache
sudo chown ${MISP_USER}:${MISP_USER} ${MISP_USER_HOME}/.cache
sudo chown ${WWW_USER}:${WWW_USER} /var/www/.cache
## Not really needed...
@ -501,14 +495,13 @@ installMISPonKali () {
cd ${PATH_TO_MISP}/app/files/scripts/python-stix
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
debug "Install maec"
debug "Installing maec"
cd ${PATH_TO_MISP}/app/files/scripts/python-maec
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
# install STIX2.0 library to support STIX 2.0 export
debug "Installing cti-python-stix2"
# install STIX2.0 library to support STIX 2.0 export:
cd ${PATH_TO_MISP}/cti-python-stix2
# Install misp-stix
debug "Installing misp-stix"
cd ${PATH_TO_MISP}/app/files/scripts/misp-stix
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
debug "Installing mixbox"
@ -536,7 +529,7 @@ installMISPonKali () {
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install zmq
debug "Installing cake"
composer
composer74
${SUDO_WWW} cp -fa ${PATH_TO_MISP}/INSTALL/setup/config.php ${PATH_TO_MISP}/app/Plugin/CakeResque/Config/config.php
@ -549,27 +542,27 @@ installMISPonKali () {
debug "Setting up database"
if [[ ! -e /var/lib/mysql/misp/users.ibd ]]; then
# Kill the anonymous users
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'localhost'"
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'localhost'"
# Because our hostname varies we'll use some Bash magic here.
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'$(hostname)'"
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'$(hostname)'"
# Kill off the demo database
sudo mysql -h $DBHOST -e "DROP DATABASE IF EXISTS test"
sudo mysql -h ${DBHOST} -e "DROP DATABASE IF EXISTS test"
# No root remote logins
sudo mysql -h $DBHOST -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
sudo mysql -h ${DBHOST} -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
# Make sure that NOBODY can access the server without a password
sudo mysqladmin -h $DBHOST -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
sudo mysqladmin -h ${DBHOST} -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
# Make our changes take effect
sudo mysql -h $DBHOST -e "FLUSH PRIVILEGES"
sudo mysql -h ${DBHOST} -e "FLUSH PRIVILEGES"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "CREATE DATABASE $DBNAME;"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT USAGE ON *.* TO $DBUSER_MISP@localhost IDENTIFIED BY '$DBPASSWORD_MISP';"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT ALL PRIVILEGES ON $DBNAME.* TO '$DBUSER_MISP'@'localhost';"
sudo mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "FLUSH PRIVILEGES;"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "CREATE DATABASE ${DBNAME};"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT USAGE ON *.* TO ${DBUSER_MISP}@localhost IDENTIFIED BY '${DBPASSWORD_MISP}';"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT ALL PRIVILEGES ON ${DBNAME}.* TO '${DBUSER_MISP}'@'localhost';"
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "FLUSH PRIVILEGES;"
enableServices
debug "Populating database"
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u $DBUSER_MISP -p$DBPASSWORD_MISP $DBNAME
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u ${DBUSER_MISP} -p${DBPASSWORD_MISP} ${DBNAME}
echo "<?php
class DATABASE_CONFIG {
@ -609,7 +602,7 @@ installMISPonKali () {
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
do
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" $PHP_INI
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" ${PHP_INI}
done
debug "Restarting Apache2"
@ -856,11 +849,12 @@ x86_64-debian-buster
x86_64-ubuntu-bionic
x86_64-ubuntu-focal
x86_64-ubuntu-hirsute
x86_64-kali-2020.4
x86_64-kali-2021.1
x86_64-kali-2021.2
x86_64-kali-2021.3
x86_64-ubuntu-jammy
x86_64-kali-2021.4
x86_64-kali-2022.1
x86_64-kali-2022.2
x86_64-kali-2022.3
x86_64-kali-2022.4
armv6l-raspbian-stretch
armv7l-raspbian-stretch
armv7l-raspbian-buster
@ -903,6 +897,12 @@ if [[ "${FLAVOUR}" == "ubuntu" ]]; then
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
installSupported PHP="7.4" && exit || exit
fi
if [[ "${RELEASE}" == "22.04" ]]; then
echo "Install on Ubuntu 22.04 LTS fully supported."
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
upgradeToPHP74
installSupported PHP="7.4" && exit || exit
fi
if [[ "${RELEASE}" == "18.10" ]]; then
echo "Install on Ubuntu 18.10 partially supported, bye."
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"

2
PyMISP

@ -1 +1 @@
Subproject commit 0dd2203c52c4c93184931480c80980806e887208
Subproject commit 795cc11881649e6082d6d8aa15498ee8710f506c

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":153}
{"major":2, "minor":4, "hotfix":163}

View File

@ -17,6 +17,7 @@ $config = array(
'user_monitoring_enabled' => false,
'authkey_keep_session' => false,
'disable_local_feed_access' => false,
'enable_svg_logos' => false,
//'auth' => array('CertAuth.Certificate'), // additional authentication methods
//'auth' => array('ShibbAuth.ApacheShibb'),
//'auth' => array('AadAuth.AadAuthenticate'),
@ -70,6 +71,7 @@ $config = array(
'enableOrgBlocklisting' => true,
'log_client_ip' => false,
'log_auth' => false,
'store_api_access_time' => false,
'disableUserSelfManagement' => false,
'disable_user_login_change' => false,
'disable_user_password_change' => false,
@ -145,7 +147,7 @@ $config = array(
'max_job_history_ttl' => 86400,
'supervisor_host' => 'localhost',
'supervisor_port' => 9001,
'supervisor_user' => '',
'supervisor_user' => 'supervisor',
'supervisor_password' => '',
),
// Uncomment the following to enable client SSL certificate authentication

View File

@ -1,14 +1,26 @@
<?php
App::uses('AppShell', 'Console/Command');
App::uses('ProcessTool', 'Tools');
App::uses('FileAccessTool', 'Tools');
App::uses('JsonTool', 'Tools');
/**
* @property Server $Server
* @property Feed $Feed
* @property Warninglist $warninglist
* @property AdminSetting $AdminSetting
* @property Taxonomy $Taxonomy
* @property Warninglist $Warninglist
* @property Attribute $Attribute
* @property Job $Job
*/
class AdminShell extends AppShell
{
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce', 'Role', 'Feed');
public $uses = [
'Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation',
'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce',
'Role', 'Feed', 'SharingGroupBlueprint', 'Correlation', 'OverCorrelatingValue'
];
public function getOptionParser()
{
@ -16,6 +28,12 @@ class AdminShell extends AppShell
$parser->addSubcommand('updateJSON', array(
'help' => __('Update the JSON definitions of MISP.'),
));
$parser->addSubcommand('updateWarningLists', array(
'help' => __('Update the JSON definition of warninglists.'),
));
$parser->addSubcommand('updateTaxonomies', array(
'help' => __('Update the JSON definition of taxonomies.'),
));
$parser->addSubcommand('setSetting', [
'help' => __('Set setting in PHP config file.'),
'parser' => [
@ -56,6 +74,9 @@ class AdminShell extends AppShell
],
],
]);
$parser->addSubcommand('dumpCurrentDatabaseSchema', [
'help' => __('Dump current database schema to JSON file.'),
]);
$parser->addSubcommand('removeOrphanedCorrelations', [
'help' => __('Remove orphaned correlations.'),
]);
@ -82,41 +103,37 @@ class AdminShell extends AppShell
public function jobGenerateCorrelation()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Generate correlation'] . PHP_EOL);
}
$jobId = $this->args[0];
$this->loadModel('Job');
$this->Job->id = $jobId;
$this->loadModel('Attribute');
$this->Attribute->generateCorrelation($jobId, 0);
$this->Job->saveField('progress', 100);
$this->Job->saveField('message', 'Job done.');
$this->Job->saveField('status', 4);
$this->Attribute->generateCorrelation($jobId);
}
public function jobGenerateOccurrences()
{
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Generate over-correlation occurrences'] . PHP_EOL);
}
$jobId = $this->args[0];
$this->OverCorrelatingValue->generateOccurrences($jobId);
}
public function jobPurgeCorrelation()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Purge correlation'] . PHP_EOL);
}
$jobId = $this->args[0];
$this->loadModel('Job');
$this->Job->id = $jobId;
$this->loadModel('Attribute');
$this->Attribute->purgeCorrelations();
$this->Job->saveField('progress', 100);
$this->Job->saveField('message', 'Job done.');
$this->Job->saveField('status', 4);
$this->Job->saveStatus($jobId);
}
public function jobGenerateShadowAttributeCorrelation()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Generate shadow attribute correlation'] . PHP_EOL);
}
@ -130,14 +147,12 @@ class AdminShell extends AppShell
public function updateMISP()
{
$this->ConfigLoad->execute();
$status = array('branch' => '2.4');
echo $this->Server->update($status) . PHP_EOL;
}
public function updateAfterPull()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Update after pull'] . PHP_EOL);
}
@ -164,7 +179,6 @@ class AdminShell extends AppShell
$this->error('This method does nothing when SimpleBackgroundJobs are enabled.');
}
$this->ConfigLoad->execute();
$this->Server->restartWorkers();
echo PHP_EOL . 'Workers restarted.' . PHP_EOL;
}
@ -175,7 +189,6 @@ class AdminShell extends AppShell
$this->error('This method does nothing when SimpleBackgroundJobs are enabled.');
}
$this->ConfigLoad->execute();
if (empty($this->args[0]) || !is_numeric($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['worker_management_tasks']['data']['Restart a worker'] . PHP_EOL);
}
@ -201,7 +214,6 @@ class AdminShell extends AppShell
$this->error('This method does nothing when SimpleBackgroundJobs are enabled.');
}
$this->ConfigLoad->execute();
if (empty($this->args[0]) || !is_numeric($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['worker_management_tasks']['data']['Kill a worker'] . PHP_EOL);
}
@ -222,7 +234,6 @@ class AdminShell extends AppShell
$this->error('This method does nothing when SimpleBackgroundJobs are enabled.');
}
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['worker_management_tasks']['data']['Start a worker'] . PHP_EOL);
}
@ -254,7 +265,6 @@ class AdminShell extends AppShell
public function updateGalaxies()
{
$this->ConfigLoad->execute();
// The following is 7.x upwards only
//$value = $this->args[0] ?? $this->args[0] ?? 0;
$value = empty($this->args[0]) ? null : $this->args[0];
@ -272,22 +282,27 @@ class AdminShell extends AppShell
public function updateTaxonomies()
{
$this->ConfigLoad->execute();
$result = $this->Taxonomy->update();
$successes = count(!empty($result['success']) ? $result['success'] : []);
$fails = count(!empty($result['fails']) ? $result['fails'] : []);
$message = '';
if ($successes == 0 && $fails == 0) {
$message = __('All taxonomies are up to date already.');
} elseif ($successes == 0 && $fails > 0) {
$successes = empty($result['success']) ? 0 : count($result['success']);
$fails = empty($result['fails']) ? 0 : count($result['fails']);
if ($successes === 0 && $fails === 0) {
$message = __('All taxonomies are up to date already.');
} elseif ($successes === 0 && $fails > 0) {
$message = __('Could not update any of the taxonomies.');
} elseif ($successes > 0 ) {
} else {
$message = __('Successfully updated %s taxonomies.', $successes);
if ($fails != 0) {
if ($fails !== 0) {
$message .= __(' However, could not update %s taxonomies.', $fails);
}
}
echo $message . PHP_EOL;
$this->out($message);
if ($fails) {
$this->out(__('Fails:'));
foreach ($result['fails'] as $fail) {
$this->out("{$fail['namespace']}: {$fail['fail']}");
}
}
}
public function enableTaxonomyTags()
@ -306,16 +321,20 @@ class AdminShell extends AppShell
public function updateWarningLists()
{
$this->ConfigLoad->execute();
$result = $this->Warninglist->update();
$success = count($result['success']);
$fails = count($result['fails']);
echo "$success warninglists updated, $fails fails" . PHP_EOL;
$this->out("$success warninglists updated, $fails fails");
if ($fails) {
$this->out(__('Fails:'));
foreach ($result['fails'] as $fail) {
$this->out("{$fail['name']}: {$fail['fail']}");
}
}
}
public function updateNoticeLists()
{
$this->ConfigLoad->execute();
$result = $this->Noticelist->update();
if ($result) {
echo 'Notice lists updated' . PHP_EOL;
@ -327,7 +346,6 @@ class AdminShell extends AppShell
# FIXME: Fails to pass userId/orgId properly, global update works.
public function updateObjectTemplates()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Update object templates'] . PHP_EOL);
} else {
@ -360,7 +378,6 @@ class AdminShell extends AppShell
public function jobUpgrade24()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Job upgrade'] . PHP_EOL);
}
@ -378,7 +395,6 @@ class AdminShell extends AppShell
public function prune_update_logs()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Prune update logs'] . PHP_EOL);
}
@ -397,7 +413,6 @@ class AdminShell extends AppShell
public function getWorkers()
{
$this->ConfigLoad->execute();
$result = $this->Server->workerDiagnostics($workerIssueCount);
$query = 'all';
if (!empty($this->args[0])) {
@ -469,7 +484,6 @@ class AdminShell extends AppShell
public function setDatabaseVersion()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Set database version'] . PHP_EOL);
} else {
@ -548,7 +562,6 @@ class AdminShell extends AppShell
public function setDefaultRole()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || !is_numeric($this->args[0])) {
$roles = $this->Role->find('list', array(
'fields' => array('id', 'name')
@ -583,7 +596,6 @@ class AdminShell extends AppShell
*/
public function change_authkey()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
echo 'MISP apikey command line tool' . PHP_EOL . 'To assign a new random API key for a user: ' . APP . 'Console/cake Admin change_authkey [user_email]' . PHP_EOL . 'To assign a fixed API key: ' . APP . 'Console/cake Admin change_authkey [user_email] [authkey]' . PHP_EOL;
die();
@ -614,7 +626,6 @@ class AdminShell extends AppShell
public function recoverSinceLastSuccessfulUpdate()
{
$this->ConfigLoad->execute();
$this->loadModel('Log');
$logs = $this->Log->find('all', array(
'conditions' => array(
@ -653,7 +664,6 @@ class AdminShell extends AppShell
public function cleanCaches()
{
$this->ConfigLoad->execute();
echo 'Cleaning caches...' . PHP_EOL;
$this->Server->cleanCacheFiles();
echo '...caches lost in time, like tears in rain.' . PHP_EOL;
@ -661,7 +671,6 @@ class AdminShell extends AppShell
public function resetSyncAuthkeys()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
echo sprintf(
__("MISP mass sync authkey reset command line tool" . PHP_EOL . "Usage: %sConsole/cake Admin resetSyncAuthkeys [user_id]" . PHP_EOL), APP
@ -687,7 +696,6 @@ class AdminShell extends AppShell
public function purgeFeedEvents()
{
$this->ConfigLoad->execute();
if (
(empty($this->args[0]) || !is_numeric($this->args[0])) ||
(empty($this->args[1]) || !is_numeric($this->args[1]))
@ -707,21 +715,18 @@ class AdminShell extends AppShell
public function dumpCurrentDatabaseSchema()
{
$this->ConfigLoad->execute();
$dbActualSchema = $this->Server->getActualDBSchema();
$dbVersion = $this->AdminSetting->getSetting('db_version');
if (!empty($dbVersion) && !empty($dbActualSchema['schema'])) {
$data = array(
$data = [
'schema' => $dbActualSchema['schema'],
'indexes' => $dbActualSchema['indexes'],
'db_version' => $dbVersion
);
$file = new File(ROOT . DS . 'db_schema.json', true);
$file->write(json_encode($data, JSON_PRETTY_PRINT) . "\n");
$file->close();
echo __("> Database schema dumped on disk") . PHP_EOL;
'db_version' => $dbVersion,
];
FileAccessTool::writeToFile(ROOT . DS . 'db_schema.json', JsonTool::encode($data, true));
$this->out(__("> Database schema dumped on disk"));
} else {
echo __("Something went wrong. Could not find the existing db version or fetch the current database schema.") . PHP_EOL;
$this->error(__('Something went wrong.'), __('Could not find the existing db version or fetch the current database schema.'));
}
}
@ -730,7 +735,6 @@ class AdminShell extends AppShell
*/
public function UserIP()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Get IPs for user ID'] . PHP_EOL);
}
@ -758,7 +762,6 @@ class AdminShell extends AppShell
*/
public function IPUser()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Get user ID for user IP'] . PHP_EOL);
}
@ -936,7 +939,7 @@ class AdminShell extends AppShell
$new = $this->params['new'] ?? null;
if ($new !== null && strlen($new) < 32) {
$this->error('New key must be at least 32 char long.');
$this->error('New key must be at least 32 chars long.');
}
if ($old === null) {
@ -945,8 +948,7 @@ class AdminShell extends AppShell
if ($new === null) {
// Generate random new key
$randomTool = new RandomTool();
$new = $randomTool->random_str();
$new = rtrim(base64_encode(random_bytes(32)), "=");
}
$this->Server->getDataSource()->begin();
@ -1143,4 +1145,70 @@ class AdminShell extends AppShell
$this->out($setting['setting'] . ': ' . $setting['errorMessage']);
}
}
public function executeSGBlueprint()
{
$id = false;
$target = 'all';
if (!empty($this->args[0])) {
$target = trim($this->args[0]);
}
if (!is_numeric($target) && !in_array($target, ['all', 'attached', 'deteached'])) {
$this->error(__('Invalid target. Either pass a blueprint ID or one of the following filters: all, attached, detached.'));
}
$conditions = [];
if (is_numeric($target)) {
$conditions['SharingGroupBlueprint']['id'] = $target;
} else if ($target === 'attached') {
$conditions['SharingGroupBlueprint']['sharing_group_id >'] = 0;
} else if ($target === 'detached') {
$conditions['SharingGroupBlueprint']['sharing_group_id'] = 0;
}
$sharingGroupBlueprints = $this->SharingGroupBlueprint->find('all', ['conditions' => $conditions, 'recursive' => 0]);
if (empty($sharingGroupBlueprints)) {
$this->error(__('No valid blueprints found.'));
}
$stats = $this->SharingGroupBlueprint->execute($sharingGroupBlueprints);
$message = __(
'Done, %s sharing group blueprint(s) matched. Sharing group changes: Created: %s. Updated: %s. Failed to create: %s.',
count($sharingGroupBlueprints),
$stats['created'],
$stats['changed'],
$stats['failed']
);
$this->out($message);
}
public function truncateTable()
{
if (!isset($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Truncate table correlation'] . PHP_EOL);
}
$userId = $this->args[0];
if ($userId) {
$user = $this->User->getAuthUser($userId);
} else {
$user = [
'id' => 0,
'email' => 'SYSTEM',
'Organisation' => [
'name' => 'SYSTEM'
]
];
}
if (empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Truncate table correlation'] . PHP_EOL);
}
if (!empty($this->args[2])) {
$jobId = $this->args[2];
}
$table = trim($this->args[1]);
$this->Correlation->truncate($user, $table);
if ($jobId) {
$this->Job->id = $jobId;
$this->Job->saveField('progress', 100);
$this->Job->saveField('date_modified', date("Y-m-d H:i:s"));
$this->Job->saveField('message', __('Database truncated: ' . $table));
}
}
}

View File

@ -32,9 +32,10 @@ class AppShell extends Shell
public function initialize()
{
parent::initialize();
$this->ConfigLoad = $this->Tasks->load('ConfigLoad');
$this->ConfigLoad->execute();
parent::initialize();
}
public function perform()

View File

@ -10,13 +10,10 @@ class AuthkeyShell extends AppShell {
public $uses = array('User', 'Log');
public $tasks = array('ConfigLoad');
public function main()
{
$this->err('This method is deprecated. Next time please use `cake user change_authkey [user] [authkey]` command.');
$this->ConfigLoad->execute();
if (!isset($this->args[0]) || empty($this->args[0])) echo 'MISP authkey reset command line tool.' . PHP_EOL . 'To assign a new authkey for a user:' . PHP_EOL . APP . 'Console/cake Authkey [email] [auth_key | optional]' . PHP_EOL;
else {
// get the users that need their password hashed

View File

@ -13,7 +13,6 @@ class BaseurlShell extends AppShell {
{
$this->err('This method is deprecated. Next time please use `cake admin setSetting MISP.baseurl [baseurl]` command.');
$this->ConfigLoad->execute();
$baseurl = $this->args[0];
$result = $this->Server->testBaseURL($baseurl);
if (true !== $result) {

View File

@ -10,11 +10,11 @@ require_once 'AppShell.php';
* @property Job $Job
* @property Tag $Tag
* @property Server $Server
* @property Correlation $Correlation
*/
class EventShell extends AppShell
{
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'Correlation', 'Tag');
public $tasks = array('ConfigLoad');
public function getOptionParser()
{
@ -39,11 +39,17 @@ class EventShell extends AppShell
'event_id' => ['help' => __('Event ID'), 'required' => true],
'user_id' => ['help' => __('User ID'), 'required' => true],
],
'options' => [
'send' => ['help' => __('Send email to given user'), 'boolean' => true],
],
],
]);
$parser->addSubcommand('duplicateTags', [
'help' => __('Show duplicate tags'),
]);
$parser->addSubcommand('generateTopCorrelations', [
'help' => __('Generate top correlations'),
]);
$parser->addSubcommand('mergeTags', [
'help' => __('Merge tags'),
'parser' => [
@ -114,7 +120,6 @@ class EventShell extends AppShell
public function doPublish()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Do publish'] . PHP_EOL);
}
@ -150,7 +155,6 @@ class EventShell extends AppShell
public function correlateValue()
{
$this->ConfigLoad->execute();
$value = $this->args[0];
if (!empty($this->args[1])) {
@ -175,7 +179,6 @@ class EventShell extends AppShell
public function cache()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Cache event'] . PHP_EOL);
}
@ -187,8 +190,9 @@ class EventShell extends AppShell
$this->Job->id = $id;
$export_type = $this->args[2];
file_put_contents('/tmp/test', $export_type);
$typeData = $this->Event->export_types[$export_type];
if (!in_array($export_type, array_keys($this->Event->export_types))) {
$exportTypes = $this->Event->exportTypes();
$typeData = $exportTypes[$export_type];
if (!in_array($export_type, array_keys($exportTypes))) {
$this->Job->saveField('progress', 100);
$timeDelta = (time()-$timeStart);
$this->Job->saveField('message', 'Job Failed due to invalid export format. (in '.$timeDelta.'s)');
@ -215,7 +219,6 @@ class EventShell extends AppShell
private function __runCaching($user, $typeData, $id, $export_type, $subType = '')
{
$this->ConfigLoad->execute();
$export_type = strtolower($typeData['type']);
$final = $this->{$typeData['scope']}->restSearch($user, $typeData['params']['returnFormat'], $typeData['params'], false, $id);
$dir = new Folder(APP . 'tmp/cached_exports/' . $export_type, true, 0750);
@ -232,7 +235,6 @@ class EventShell extends AppShell
public function cachebro()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Cache bro'] . PHP_EOL);
}
@ -273,7 +275,6 @@ class EventShell extends AppShell
public function alertemail()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Alert email'] . PHP_EOL);
}
@ -281,7 +282,7 @@ class EventShell extends AppShell
$userId = $this->args[0];
$jobId = $this->args[1];
$eventId = $this->args[2];
$oldpublish = $this->args[3];
$oldpublish = isset($this->args[3]) ? $this->args[3] : null;
$user = $this->getUser($userId);
$this->Event->sendAlertEmail($eventId, $user, $oldpublish, $jobId);
}
@ -306,7 +307,6 @@ class EventShell extends AppShell
public function postsemail()
{
$this->ConfigLoad->execute();
if (
empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) ||
empty($this->args[3]) || empty($this->args[4]) || empty($this->args[5])
@ -340,7 +340,6 @@ class EventShell extends AppShell
public function enqueueCaching()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Enqueue caching'] . PHP_EOL);
}
@ -385,7 +384,7 @@ class EventShell extends AppShell
// the special cache files containing all events
$i = 0;
foreach ($users as $user) {
foreach ($this->Event->export_types as $k => $type) {
foreach ($this->Event->exportTypes() as $k => $type) {
if ($k == 'stix') continue;
$this->Job->cache($k, $user['User']);
$i++;
@ -397,7 +396,6 @@ class EventShell extends AppShell
public function publish()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[2]) || empty($this->args[3])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Publish event'] . PHP_EOL);
}
@ -439,7 +437,7 @@ class EventShell extends AppShell
}
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
$result = $this->Event->publish_sightings($id, $passAlong, $sightingsUuidsToPush);
$result = $this->Event->publishSightings($id, $passAlong, $sightingsUuidsToPush);
$count = count($sightingsUuidsToPush);
$message = $count === 0 ? "All sightings published" : "$count sightings published";
@ -457,7 +455,6 @@ class EventShell extends AppShell
public function publish_galaxy_clusters()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || !array_key_exists(3, $this->args)) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Publish Galaxy clusters'] . PHP_EOL);
}
@ -484,7 +481,6 @@ class EventShell extends AppShell
public function enrichment()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Run enrichment'] . PHP_EOL);
}
@ -580,7 +576,6 @@ class EventShell extends AppShell
public function recoverEvent()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Recover event'] . PHP_EOL);
}
@ -602,6 +597,7 @@ class EventShell extends AppShell
public function testEventNotificationEmail()
{
list($eventId, $userId) = $this->args;
$send = $this->param('send');
$user = $this->getUser($userId);
$eventForUser = $this->Event->fetchEvent($user, [
@ -621,10 +617,16 @@ class EventShell extends AppShell
App::uses('SendEmail', 'Tools');
App::uses('GpgTool', 'Tools');
$sendEmail = new SendEmail(GpgTool::initializeGpg());
$sendEmail->setTransport('Debug');
if (!$send) {
$sendEmail->setTransport('Debug');
}
$result = $sendEmail->sendToUser(['User' => $user], null, $emailTemplate);
echo $result['contents']['headers'] . "\n\n" . $result['contents']['message'] . "\n";
if ($send) {
var_dump($result);
} else {
echo $result['contents']['headers'] . "\n\n" . $result['contents']['message'] . "\n";
}
}
/**
@ -643,17 +645,20 @@ class EventShell extends AppShell
public function generateTopCorrelations()
{
$this->ConfigLoad->execute();
$jobId = $this->args[0];
$job = $this->Job->read(null, $jobId);
$job['Job']['progress'] = 1;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Generating top correlations list.');
$this->Job->save($job);
$result = $this->Correlation->generateTopCorrelations($jobId);
$job['Job']['progress'] = 100;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Job done.');
$this->Job->save($job);
$jobId = $this->args[0] ?? null;
if ($jobId) {
$job = $this->Job->read(null, $jobId);
$job['Job']['progress'] = 1;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Generating top correlations list.');
$this->Job->save($job);
}
$this->Correlation->generateTopCorrelations($jobId);
if ($jobId) {
$job['Job']['progress'] = 100;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Job done.');
$this->Job->save($job);
}
}
}

View File

@ -10,7 +10,6 @@ class LiveShell extends AppShell {
public function main()
{
$this->ConfigLoad->execute();
$live = $this->args[0];
if ($live != 0 && $live != 1) {
echo 'Invalid parameters. Usage: /var/www/MISP/app/Console/cake Live [0|1]';

View File

@ -0,0 +1,563 @@
<?php
class Ls22Shell extends AppShell
{
public $uses = ['Server'];
private $__servers = [];
private function __getInstances($path)
{
if (empty($path)) {
$path = 'instances.csv';
}
$file = file_get_contents($path);
$lines = explode(PHP_EOL, $file);
foreach ($lines as $k => $line) {
if ($k === 0) {
continue;
}
$fields = explode(',', $line);
if (count($fields) === 4 && $fields[1] === 'admin@admin.test') {
$this->__servers[] = [
'Server' => [
'url' => trim($fields[0]),
'authkey' => trim($fields[2])
]
];
}
}
}
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->addSubcommand('enableTaxonomy', [
'help' => __('Enable a taxonomy with all its tags.'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
],
'taxonomy' => [
'help' => 'The name of the taxonomy to enable, such as "tlp"',
'short' => 't',
'required' => true
],
'misp_url_filter' => [
'help' => 'The url of the instance to enable it for - otherwise all are selected',
'short' => 'm',
'required' => false
]
),
),
]);
$parser->addSubcommand('checkSyncConnections', [
'help' => __('Check the given sync connection(s) for the given server(s).'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
],
'misp_url_filter' => [
'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
'short' => 'm',
'required' => false
],
'synced_misp_url_filter' => [
'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
'short' => 's',
'required' => false
]
),
),
]);
$parser->addSubcommand('modifySyncConnection', [
'help' => __('Modify sync connection(s).'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
],
'misp_url_filter' => [
'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
'short' => 'm',
'required' => false
],
'synced_misp_url_filter' => [
'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
'short' => 's',
'required' => false
],
'json' => [
'help' => 'JSON delta to push (such as \'{"push": 1}\').',
'short' => 'j',
'required' => true
]
),
),
]);
$parser->addSubcommand('addWarninglist', [
'help' => __('Inject warninglist'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
],
'warninglist' => [
'help' => 'Path to the warninglist file',
'short' => 'w',
'required' => true
]
),
),
]);
$parser->addSubcommand('status', [
'help' => __('Check if the instances are available / the API key works.'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
]
),
),
]);
$parser->addSubcommand('scores', [
'help' => __('Generate the scores for all BTs.'),
'parser' => array(
'options' => array(
'instances' => [
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
'short' => 'i',
'required' => true
],
'server_url' => [
'help' => 'URL of the server to query for the scores. If nothing is specified, the first valid entry from instances.csv is taken.',
'short' => 's',
'required' => false
],
'from' => [
'help' => 'Lower bound of the date. Accepts timestamp or date distance (such as 1d or 5h). Defaults to unbounded.',
'short' => 'f',
'required' => false
],
'to' => [
'help' => 'Upper bound of the date. Accepts timestamp or date distance (such as 1d or 5h). Defaults to unbounded.',
'short' => 't',
'required' => false
],
'org' => [
'help' => 'Name the org that should be evaluated. If not set, all will be included.',
'short' => 'o',
'required' => false
]
),
),
]);
return $parser;
}
public function checkSyncConnections()
{
$this->__getInstances($this->param('instances'));
$results = [];
$instanceFilter = $this->param('misp_url_filter');
$syncedInstanceFilter = $this->param('synced_misp_url_filter');
foreach ($this->__servers as $server) {
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
continue;
}
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$start_time = microtime(true);
$response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
$baseline = round((microtime(true) - $start_time) * 1000);
if (!$response->isOk()) {
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
continue;
}
$synced_servers = json_decode($response->body, true);
foreach ($synced_servers as $synced_server) {
$success = false;
if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
$start_time = microtime(true);
$response = $HttpSocket->get($server['Server']['url'] . '/servers/testConnection/' . $synced_server['Server']['id'], '{}', $request);
$execution_time = round((microtime(true) - $start_time) * 1000) - $baseline;
if ($response->isOk()) {
$success = true;
}
$this->out(
sprintf(
'%s connection to %s: %s (%sms)',
$server['Server']['url'],
$synced_server['Server']['url'],
sprintf(
'<%s>%s</%s>',
$success ? 'info' : 'error',
$success ? 'Success' : 'Failed',
$success ? 'info' : 'error'
),
$execution_time
),
1,
Shell::NORMAL
);
}
}
}
}
public function modifySyncConnection()
{
$this->__getInstances($this->param('instances'));
$results = [];
$instanceFilter = $this->param('misp_url_filter');
$syncedInstanceFilter = $this->param('synced_misp_url_filter');
$json = $this->param('json');
foreach ($this->__servers as $server) {
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
continue;
}
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
if (!$response->isOk()) {
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
}
$synced_servers = json_decode($response->body, true);
$success = false;
foreach ($synced_servers as $synced_server) {
if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
debug($json);
$response = $HttpSocket->post($server['Server']['url'] . '/servers/edit/' . $synced_server['Server']['id'], $json, $request);
debug($response->body);
if ($response->isOk()) {
$success = true;
}
$this->out(
sprintf(
'%s connection to %s: %s',
$server['Server']['url'],
$synced_server['Server']['url'],
sprintf(
'<%s>%s</%s>',
$success ? 'info' : 'error',
$success ? 'Success' : 'Failed',
$success ? 'info' : 'error'
)
),
1,
Shell::NORMAL
);
}
}
}
}
public function enableTaxonomy()
{
$taxonomyToEnable = $this->param('taxonomy');
$instanceFilter = $this->param('misp_url_filter');
if (empty($taxonomyToEnable)) {
$this->error('No taxonomy provided', 'Provide a taxonomy by specifying the -t or --taxonomy options.');
}
$this->__getInstances($this->param('instances'));
$results = [];
foreach ($this->__servers as $server) {
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
continue;
}
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$response = $HttpSocket->get($server['Server']['url'] . '/taxonomies/index', false, $request);
if (!$response->isOk()) {
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
}
$taxonomies = json_decode($response->body, true);
$success = false;
foreach ($taxonomies as $taxonomy) {
if ($taxonomy['Taxonomy']['namespace'] === $taxonomyToEnable) {
$response = $HttpSocket->post($server['Server']['url'] . '/taxonomies/enable/' . $taxonomy['Taxonomy']['id'], '{}', $request);
if ($response->isOk()) {
$response = $HttpSocket->post($server['Server']['url'] . '/taxonomies/addTag/' . $taxonomy['Taxonomy']['id'], '{}', $request);
if ($response->isOk()) {
$success = true;
}
}
}
}
$results[$server['Server']['url']] = $success ? 'Success' : 'Failed';
$statusWrapped = sprintf(
'<%s>%s</%s>',
$success ? 'info' : 'error',
$results[$server['Server']['url']],
$success ? 'info' : 'error'
);
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
}
}
public function status()
{
$this->__getInstances($this->param('instances'));
$results = [];
foreach ($this->__servers as $server) {
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$start_time = microtime(true);
$fatal_error = false;
try {
$response = $HttpSocket->get($server['Server']['url'] . '/users/view/me', false, $request);
} catch (Exception $e) {
$fatal_error = true;
echo "\x07";
$statusWrapped = sprintf(
'<error>%s %s: %s</error>',
'Something went wrong while trying to reach',
$server['Server']['url'],
$e->getMessage()
);
}
if (!$fatal_error) {
$execution_time = round((microtime(true) - $start_time) * 1000);
$statusWrapped = sprintf(
'<%s>%s</%s>',
$response->isOk() ? 'info' : 'error',
$response->isOk() ? 'OK (' . $execution_time . 'ms)' : 'Failed. (' . $response->code . ')',
$response->isOk() ? 'info' : 'error'
);
}
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
}
}
public function addWarninglist()
{
$path = $this->param('warninglist');
if (empty($path)) {
$this->error('No warninglist provided', 'Provide a path to a file containing a warninglist JSON by specifying the -w or --warninglist options.');
}
$file = file_get_contents($path);
$this->__getInstances($this->param('instances'));
$results = [];
foreach ($this->__servers as $server) {
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$start_time = microtime(true);
$response = $HttpSocket->post($server['Server']['url'] . '/warninglists/add', $file, $request);
$statusWrapped = sprintf(
'<%s>%s</%s>',
$response->isOk() ? 'info' : 'error',
$response->isOk() ? 'OK' : 'Could not create warninglist',
$response->isOk() ? 'info' : 'error'
);
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
}
}
public function scores()
{
$results = [];
$this->__getInstances($this->param('instances'));
$server = null;
if (!empty($this->param['server_url'])) {
foreach ($this->__servers as $temp_server) {
if ($temp_server['Server']['url'] === $this->param['server_url']) {
$server = $temp_server;
}
}
} else {
$server = $this->__servers[0];
}
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server);
$response = $HttpSocket->get($server['Server']['url'] . '/organisations/index/scope:all', false, $request);
$orgs = json_decode($response->body(), true);
$this->out(__('Organisations fetched. %d found.', count($orgs)), 1, Shell::VERBOSE);
$org_mapping = [];
foreach ($orgs as $org) {
if (!empty($this->param('org')) && $org['Organisation']['name'] !== $this->param('org')) {
continue;
}
if ($org['Organisation']['name'] === 'YT') {
continue;
}
$org_mapping[$org['Organisation']['name']] = $org['Organisation']['id'];
}
if (!empty($this->param['from'])) {
$time_range[] = $this->param['from'];
}
if (!empty($this->param['to'])) {
if (empty($time_range)) {
$time_range[] = '365d';
}
$time_range[] = $this->param['to'];
}
foreach ($org_mapping as $org_name => $org_id) {
$time_range = [];
$params = [
'org' => $org_id
];
if (!empty($time_range)) {
$params['publish_timestamp'] = $time_range;
}
$response = $HttpSocket->post($server['Server']['url'] . '/events/restSearch', json_encode($params), $request);
$events = json_decode($response->body(), true);
$this->out(__('Events fetched from %s. %d found.', $org_name, count($events['response'])), 1, Shell::VERBOSE);
$results[$org_name] = [
'attribute_count' => 0,
'object_count' => 0,
'connected_elements' => 0,
'event_tags' => 0,
'attribute_tags' => 0,
'attack' => 0,
'other' => 0,
'attribute_attack' => 0,
'attribute_other' => 0,
'score' => 0,
'warnings' => 0
];
foreach ($events['response'] as $event) {
if (!empty($event['Event']['Tag'])) {
foreach ($event['Event']['Tag'] as $tag) {
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
$results[$org_name]['attack'] += 1;
} else {
$results[$org_name]['other'] += 1;
}
}
}
if (!empty($event['Event']['Galaxy'])) {
foreach ($event['Event']['Galaxy'] as $galaxy) {
if ($galaxy['type'] === 'mitre-attack-pattern') {
$results[$org_name]['attack'] += 1;
} else {
$results[$org_name]['other'] += 1;
}
}
}
foreach ($event['Event']['Attribute'] as $attribute) {
if (!empty($attribute['referenced_by'])) {
$results[$org_name]['connected_elements'] +=1;
}
if (!empty($attribute['Tag'])) {
foreach ($attribute['Tag'] as $tag) {
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
$results[$org_name]['attribute_attack'] += 1;
} else {
$results[$org_name]['attribute_other'] += 1;
}
}
}
if (!empty($attribute['warnings'])) {
$result[$org_name]['warnings'] += 1;
}
}
$results[$org_name]['attribute_count'] += count($event['Event']['Attribute']);
if (!empty($event['Event']['Object'])) {
foreach ($event['Event']['Object'] as $object) {
$results[$org_name]['attribute_count'] += count($object['Attribute']);
$results[$org_name]['object_count'] += 1;
if (!empty($object['ObjectReference'])) {
$results[$org_name]['connected_elements'] += 1;
}
foreach ($object['Attribute'] as $attribute) {
if (!empty($attribute['Tag'])) {
foreach ($attribute['Tag'] as $tag) {
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
$results[$org_name]['attribute_attack'] += 1;
} else {
$results[$org_name]['attribute_other'] += 1;
}
}
}
}
}
}
}
}
$scores = [];
foreach ($results as $k => $result) {
$totalCount = $result['attribute_count'] + $result['object_count'];
if ($totalCount) {
if (empty($result['warnings'])) {
$results[$k]['metrics']['warnings'] = 100;
} else if (100 * $result['warnings'] < $result['attribute_count']) {
$results[$k]['metrics']['warnings'] = 50;
} else {
$results[$k]['metrics']['warnings'] = 0;
}
$results[$k]['metrics']['connectedness'] = 100 * ($result['connected_elements'] / ($result['attribute_count'] + $result['object_count']));
$results[$k]['metrics']['attack_weight'] = 100 * (2*($result['attack']) + $result['attribute_attack']) / ($result['attribute_count'] + $result['object_count']);
$results[$k]['metrics']['other_weight'] = 100 * (2*($result['other']) + $result['attribute_other']) / ($result['attribute_count'] + $result['object_count']);
}
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings'] as $metric) {
if (empty($results[$k]['metrics'][$metric])) {
$results[$k]['metrics'][$metric] = 0;
}
if ($results[$k]['metrics'][$metric] > 100) {
$results[$k]['metrics'][$metric] = 100;
}
}
$results[$k]['score'] = round(
20 * $results[$k]['metrics']['warnings'] +
20 * $results[$k]['metrics']['connectedness'] +
40 * $results[$k]['metrics']['attack_weight'] +
20 * $results[$k]['metrics']['other_weight']
) / 100;
$scores[$k]['total'] = $results[$k]['score'];
$scores[$k]['warnings'] = round(20 * $results[$k]['metrics']['warnings']);
$scores[$k]['connectedness'] = round(20 * $results[$k]['metrics']['connectedness']);
$scores[$k]['attack_weight'] = round(40 * $results[$k]['metrics']['attack_weight']);
$scores[$k]['other_weight'] = round(20 * $results[$k]['metrics']['other_weight']);
}
arsort($scores, SORT_DESC);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
$this->out(sprintf(
'| %s | %s | %s |',
str_pad('Org', 10, ' ', STR_PAD_RIGHT),
str_pad('Graph', 100, ' ', STR_PAD_RIGHT),
str_pad('Score', 8, ' ', STR_PAD_RIGHT)
), 1, Shell::NORMAL);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
foreach ($scores as $org => $score) {
$score_string[0] = str_repeat('█', round($score['warnings']/100));
$score_string[1] = str_repeat('█', round($score['connectedness']/100));
$score_string[2] = str_repeat('█', round($score['attack_weight']/100));
$score_string[3] = str_repeat('█', round($score['other_weight']/100));
$this->out(sprintf(
'| %s | %s | %s |',
str_pad($org, 10, ' ', STR_PAD_RIGHT),
sprintf(
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info>%s',
$score_string[0],
$score_string[1],
$score_string[2],
$score_string[3],
str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
),
str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
), 1, Shell::NORMAL);
}
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
$this->out(sprintf(
'| Legend: %s %s %s %s %s |',
'<error>█: Warnings</error>',
'<warning>█: Connectedness</warning>',
'<question>█: ATT&CK context</question>',
'<info>█: Other Context</info>',
str_repeat(' ', 52)
), 1, Shell::NORMAL);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
file_put_contents(APP . 'tmp/report.json', json_encode($results, JSON_PRETTY_PRINT));
}
}

View File

@ -14,7 +14,6 @@ class PasswordShell extends AppShell {
{
$this->err('This method is deprecated. Next time please use `cake user change_pw [user] [password]` command.');
$this->ConfigLoad->execute();
if (!isset($this->args[0]) || empty($this->args[0]) || !isset($this->args[1]) || empty($this->args[1])) echo 'MISP password reset command line tool.' . PHP_EOL . 'To assign a new password for a user:' . PHP_EOL . APP . 'Console/cake Password [email] [password]' . PHP_EOL;
else {
// get the users that need their password hashed
@ -42,7 +41,6 @@ class PasswordShell extends AppShell {
public function getOptionParser()
{
$this->ConfigLoad->execute();
$parser = parent::getOptionParser();
$parser->addOption('override_password_change', array(
'short' => 'o',

View File

@ -14,6 +14,20 @@ class ServerShell extends AppShell
{
public $uses = array('Server', 'Task', 'Job', 'User', 'Feed');
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->addSubcommand('fetchIndex', [
'help' => __('Fetch remote instance event index.'),
'parser' => array(
'arguments' => array(
'server_id' => ['help' => __('Remote server ID.'), 'required' => true],
),
)
]);
return $parser;
}
public function list()
{
$servers = $this->Server->find('all', [
@ -55,9 +69,17 @@ class ServerShell extends AppShell
echo $this->json($res) . PHP_EOL;
}
public function fetchIndex()
{
$serverId = intval($this->args[0]);
$server = $this->getServer($serverId);
$serverSync = new ServerSyncTool($server, $this->Server->setupSyncRequest($server));
$index = $this->Server->getEventIndexFromServer($serverSync);
echo $this->json($index) . PHP_EOL;
}
public function pullAll()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['PullAll'] . PHP_EOL);
}
@ -367,7 +389,6 @@ class ServerShell extends AppShell
public function enqueuePull()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue pull'] . PHP_EOL);
}
@ -430,7 +451,6 @@ class ServerShell extends AppShell
public function enqueueFeedFetch()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue feed fetch'] . PHP_EOL);
}
@ -480,7 +500,6 @@ class ServerShell extends AppShell
public function enqueueFeedCache()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue feed cache'] . PHP_EOL);
}
@ -537,7 +556,6 @@ class ServerShell extends AppShell
public function enqueuePush()
{
$this->ConfigLoad->execute();
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue push'] . PHP_EOL);
}
@ -578,6 +596,36 @@ class ServerShell extends AppShell
$this->Task->saveField('message', count($servers) . ' job(s) completed at ' . date('d/m/Y - H:i:s') . '.');
}
public function sendPeriodicSummaryToUsers()
{
$periods = $this->__getPeriodsForToday();
$start_time = time();
echo __n('Started periodic summary generation for the %s period', 'Started periodic summary generation for periods: %s', count($periods), implode(', ', $periods)) . PHP_EOL;
foreach ($periods as $period) {
$users = $this->User->getSubscribedUsersForPeriod($period);
echo __n('%s user has subscribed for the `%s` period', '%s users has subscribed for the `%s` period', count($users), count($users), $period) . PHP_EOL;
foreach ($users as $user) {
echo __('Sending `%s` report to `%s`', $period, $user['User']['email']) . PHP_EOL;
$emailTemplate = $this->User->generatePeriodicSummary($user['User']['id'], $period, false);
$this->User->sendEmail($user, $emailTemplate, false, null);
}
}
echo __('All reports sent. Task took %s seconds', time() - $start_time) . PHP_EOL;
}
private function __getPeriodsForToday(): array
{
$today = new DateTime();
$periods = ['daily'];
if ($today->format('j') == 1) {
$periods[] = 'monthly';
}
if ($today->format('N') == 1) {
$periods[] = 'weekly';
}
return $periods;
}
/**
* @param int $userId
* @return array

View File

@ -78,7 +78,9 @@ class StartWorkerShell extends AppShell
try {
$job->setStatus(BackgroundJob::STATUS_RUNNING);
CakeLog::info("[JOB ID: {$job->id()}] - started.");
$command = implode(' ', array_merge([$job->command()], $job->args()));
CakeLog::info("[JOB ID: {$job->id()}] - started command `$command`.");
$this->BackgroundJobsTool->update($job);
$job->run();

View File

@ -1,5 +1,3 @@
http://download.geonames.org/export/dump/countryInfo.txt
<?php
class SupportShell extends AppShell {

View File

@ -14,6 +14,9 @@ class UserShell extends AppShell
$parser->addSubcommand('list', [
'help' => __('Get list of user accounts.'),
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
],
'options' => [
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
],
@ -46,6 +49,18 @@ class UserShell extends AppShell
],
],
]);
$parser->addSubcommand('check_validity', [
'help' => __('Check users validity from external identity provider and block not valid user.'),
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
],
'options' => [
'block_invalid' => ['help' => __('Block user that are considered invalid.'), 'boolean' => true],
'update' => ['help' => __('Update user role or organisation.'), 'boolean' => true],
],
]
]);
$parser->addSubcommand('change_pw', [
'help' => __('Change user password.'),
'parser' => [
@ -63,6 +78,7 @@ class UserShell extends AppShell
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
'authKey' => ['help' => __('Optional new authentication key.'), 'required' => false],
],
],
]);
@ -93,6 +109,17 @@ class UserShell extends AppShell
public function list()
{
$userId = isset($this->args[0]) ? $this->args[0] : null;
if ($userId) {
$conditions = ['OR' => [
'User.id' => $userId,
'User.email LIKE' => "%$userId%",
'User.sub LIKE' => "%$userId%",
]];
} else {
$conditions = [];
}
if ($this->params['json']) {
// do not fetch sensitive or big values
$schema = $this->User->schema();
@ -108,6 +135,7 @@ class UserShell extends AppShell
$users = $this->User->find('all', [
'recursive' => -1,
'fields' => $fields,
'conditions' => $conditions,
'contain' => ['Organisation', 'Role', 'UserSetting'],
]);
@ -115,6 +143,7 @@ class UserShell extends AppShell
} else {
$users = $this->User->find('column', [
'fields' => ['email'],
'conditions' => $conditions,
]);
foreach ($users as $user) {
$this->out($user);
@ -237,6 +266,55 @@ class UserShell extends AppShell
$this->out("User $userId unblocked.");
}
public function check_validity()
{
$auth = Configure::read('Security.auth');
if (!$auth) {
$this->error('External authentication is not enabled');
}
if (!is_array($auth)) {
throw new Exception("`Security.auth` config value must be array.");
}
if (!in_array('OidcAuth.Oidc', $auth, true)) {
$this->error('This method is currently supported just by OIDC auth provider');
}
App::uses('Oidc', 'OidcAuth.Lib');
$oidc = new Oidc($this->User);
$conditions = ['disabled' => false]; // fetch just not disabled users
$userId = isset($this->args[0]) ? $this->args[0] : null;
if ($userId) {
$conditions['OR'] = [
'User.id' => $userId,
'User.email LIKE' => "%$userId%",
'User.sub LIKE' => "%$userId%",
];
}
$users = $this->User->find('all', [
'recursive' => -1,
'contain' => ['UserSetting'],
'conditions' => $conditions,
]);
$blockInvalid = $this->params['block_invalid'];
$update = $this->params['update'];
foreach ($users as $user) {
$user['User']['UserSetting'] = $user['UserSetting'];
$user = $user['User'];
if ($blockInvalid) {
$result = $oidc->blockInvalidUser($user, true, $update);
} else {
$result = $oidc->isUserValid($user, true, $update);
}
$this->out("{$user['email']}: " . ($result ? '<success>valid</success>' : '<error>invalid</error>'));
}
}
public function change_pw()
{
list($userId, $newPassword) = $this->args;
@ -257,12 +335,24 @@ class UserShell extends AppShell
public function change_authkey()
{
list($userId) = $this->args;
$newkey = null;
if (isset($this->args[1])) {
list($userId, $newkey) = $this->args;
} else {
list($userId) = $this->args;
}
$user = $this->getUser($userId);
# validate new authentication key if provided
if (!empty($newkey) && (strlen($newkey) != 40 || !ctype_alnum($newkey))) {
$this->error('The new auth key needs to be 40 characters long and only alphanumeric.');
}
if (empty(Configure::read('Security.advanced_authkeys'))) {
$oldKey = $user['authkey'];
$newkey = $this->User->generateAuthKey();
if (empty($newkey)) {
$newkey = $this->User->generateAuthKey();
}
$this->User->updateField($user, 'authkey', $newkey);
$this->Log->createLogEntry('SYSTEM', 'reset_auth_key', 'User', $user['id'],
__('Authentication key for user %s (%s) updated.', $user['id'], $user['email']),
@ -270,7 +360,7 @@ class UserShell extends AppShell
);
$this->out("Authentication key changed to: $newkey");
} else {
$newkey = $this->User->AuthKey->resetAuthKey($user['id']);
$newkey = $this->User->AuthKey->resetAuthKey($user['id'], null, $newkey);
if ($newkey) {
$this->out("Old authentication keys disabled and new key created: $newkey");
} else {

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
require_once 'AppShell.php';
class WorkflowShell extends AppShell {
public $uses = ['Job', 'Workflow'];
public function executeWorkflowForTrigger()
{
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || empty($this->args[3])) {
die(__('Invalid number of arguments.'));
}
$trigger_id = $this->args[0];
$data = JsonTool::decode($this->args[1]);
$logging = JsonTool::decode($this->args[2]);
$jobId = $this->args[3];
$blockingErrors = [];
$executionSuccess = $this->Workflow->executeWorkflowForTrigger($trigger_id, $data, $blockingErrors);
$job = $this->Job->read(null, $jobId);
$job['Job']['progress'] = 100;
$job['Job']['status'] = Job::STATUS_COMPLETED;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
if ($executionSuccess) {
$job['Job']['message'] = __('Workflow for trigger `%s` completed execution', $trigger_id);
} else {
$errorMessage = implode(', ', $blockingErrors);
$message = __('Error while executing workflow for trigger `%s`: %s. %s%s', $trigger_id, $logging['message'], PHP_EOL . __('Returned message: %s', $errorMessage));
$job['Job']['message'] = $message;
}
$this->Job->save($job);
}
public function walkGraph()
{
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || empty($this->args[3])) {
die(__('Invalid number of arguments.'));
}
$workflow_id = (int)$this->args[0];
$workflow = $this->Workflow->fetchWorkflow($workflow_id);
$node_id_to_exec = (int)$this->args[1];
$roamingData = JsonTool::decode($this->args[2]);
$for_path = $this->args[3];
$jobId = $this->args[4];
$concurrentErrors = [];
$walkResult = [];
$executionSuccess = $this->Workflow->walkGraph(
$workflow,
$node_id_to_exec,
$for_path,
$roamingData,
$concurrentErrors,
$walkResult
);
$job = $this->Job->read(null, $jobId);
$job['Job']['progress'] = 100;
$job['Job']['status'] = Job::STATUS_COMPLETED;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
if ($executionSuccess) {
$job['Job']['message'] = __('Workflow concurrent task executed %s nodes starting from node %s.', count($walkResult['executed_nodes']), $node_id_to_exec);
} else {
$message = __('Error while executing workflow concurrent task. %s', PHP_EOL . implode(', ', $concurrentErrors));
$this->Workflow->logExecutionError($workflow, $message);
$job['Job']['message'] = $message;
}
$this->Job->save($job);
}
}

View File

@ -0,0 +1,302 @@
<?php
App::uses('AppController', 'Controller');
class ApiController extends AppController
{
public function beforeFilter()
{
parent::beforeFilter();
$this->Security->unlockedActions[] = 'getApiInfo';
}
public function openapi()
{
$this->set('title_for_layout', __('OpenAPI'));
}
public function viewDeprecatedFunctionUse()
{
$server = ClassRegistry::init('Server');
$data = $this->Deprecation->getDeprecatedAccessList($server);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->layout = false;
$this->set('data', $data);
}
}
public function getAllApis()
{
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
$allValidApisFieldsConstraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
$output = [
'allValidApis' => $allValidApis,
'fieldsConstraint' => $allValidApisFieldsConstraint,
];
return $this->RestResponse->viewData($output, 'json');
}
public function getApiInfo()
{
$relative_path = $this->request->data['url'];
$result = $this->RestResponse->getApiInfo($relative_path);
if ($this->_isRest()) {
if (!empty($result)) {
$result['api_info'] = $result;
}
return $this->RestResponse->viewData($result, $this->response->type());
} else {
if (empty($result)) {
return $this->RestResponse->viewData('&nbsp;', $this->response->type());
}
$this->layout = false;
$this->autoRender = false;
$this->set('api_info', $result);
$this->render('ajax/get_api_info');
}
}
public function rest()
{
if ($this->request->is('post')) {
$request = $this->request->data;
if (!empty($request['Server'])) {
$request = $this->request->data['Server'];
}
$curl = '';
$python = '';
try {
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
}
} catch (Exception $e) {
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
}
}
$header = sprintf(
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
__('YOUR_API_KEY')
);
$this->set('header', $header);
$allAccessibleApis = $this->RestResponse->getAccessibleApis($this->Auth->user());
$this->set('allAccessibleApis', $allAccessibleApis);
$this->set('title_for_layout', __('REST client'));
}
/**
* @param array $request
* @param string $curl
* @param string $python
* @return array|false
*/
private function __doRestQuery(array $request, &$curl = false, &$python = false)
{
$params = array();
$logHeaders = $request['header'];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$logHeaders = explode("\n", $request['header']);
foreach ($logHeaders as $k => $header) {
if (strpos($header, 'Authorization') !== false) {
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
}
}
$logHeaders = implode("\n", $logHeaders);
}
if (empty($request['body'])) {
$historyBody = '';
} else if (strlen($request['body']) > 65535) {
$historyBody = ''; // body is too long to save into history table
} else {
$historyBody = $request['body'];
}
$rest_history_item = array(
'headers' => $logHeaders,
'body' => $historyBody,
'url' => $request['url'],
'http_method' => $request['method'],
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
'show_result' => $request['show_result'],
'skip_ssl' => $request['skip_ssl_validation'],
'bookmark' => $request['bookmark'],
'bookmark_name' => $request['name'],
'timestamp' => time(),
);
if (!empty($request['url'])) {
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
unset($request['url']);
} else {
$url = $request['url'];
}
} else {
throw new InvalidArgumentException('URL not set.');
}
if (!empty($request['skip_ssl_validation'])) {
$params['ssl_verify_peer'] = false;
$params['ssl_verify_host'] = false;
$params['ssl_verify_peer_name'] = false;
$params['ssl_allow_self_signed'] = true;
}
$params['timeout'] = 300;
App::uses('HttpSocketExtended', 'Tools');
$HttpSocket = new HttpSocketExtended($params);
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
$request['header'] = array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => 'MISP REST Client',
);
foreach ($temp_headers as $header) {
$header = explode(':', $header);
$header[0] = trim($header[0]);
$header[1] = trim($header[1]);
$request['header'][$header[0]] = $header[1];
}
$start = microtime(true);
if (
!empty($request['method']) &&
$request['method'] === 'GET'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('get', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'POST' &&
!empty($request['body'])
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('post', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'DELETE'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('delete', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
} else {
return false;
}
$viewData = [
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
'url' => $url,
'code' => $response->code,
'headers' => $response->headers,
];
if (!empty($request['show_result'])) {
$viewData['data'] = $response->body;
} else {
if ($response->isOk()) {
$viewData['data'] = 'Success.';
} else {
$viewData['data'] = 'Something went wrong.';
}
}
$rest_history_item['outcome'] = $response->code;
$this->loadModel('RestClientHistory');
$this->RestClientHistory->insert($this->Auth->user(), $rest_history_item);
return $viewData;
}
private function __generatePythonScript(array $request, $url)
{
$slashCounter = 0;
$baseurl = '';
$relative = '';
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
for ($i = 0; $i < strlen($url); $i++) {
//foreach ($url as $url[$i]) {
if ($url[$i] === '/') {
$slashCounter += 1;
if ($slashCounter == 3) {
continue;
}
}
if ($slashCounter < 3) {
$baseurl .= $url[$i];
} else {
$relative .= $url[$i];
}
}
$python_script =
sprintf(
'misp_url = \'%s\'
misp_key = \'%s\'
misp_verifycert = %s
relative_path = \'%s\'
body = %s
from pymisp import ExpandedPyMISP
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
misp.direct_call(relative_path, body)
',
$baseurl,
$request['header']['Authorization'],
$verifyCert,
$relative,
(empty($request['body']) ? 'None' : $request['body'])
);
return $python_script;
}
private function __generateCurlQuery($type, array $request, $url)
{
if ($type === 'get') {
$curl = sprintf(
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
} else {
$curl = sprintf(
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
PHP_EOL,
json_encode(json_decode($request['body'])),
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
}
return $curl;
}
}

View File

@ -4,6 +4,7 @@ App::uses('Controller', 'Controller');
App::uses('File', 'Utility');
App::uses('RequestRearrangeTool', 'Tools');
App::uses('BlowfishConstantPasswordHasher', 'Controller/Component/Auth');
App::uses('BetterCakeEventManager', 'Tools');
/**
* Application Controller
@ -34,8 +35,8 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '136';
public $pyMispVersion = '2.4.152';
private $__queryVersion = '145';
public $pyMispVersion = '2.4.162';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';
@ -48,14 +49,6 @@ class AppController extends Controller
public $restResponsePayload = null;
// Used for _isAutomation(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
// This is used to allow authentication via headers for methods not covered by _isRest() - as that only checks for JSON and XML formats
public $automationArray = array(
'events' => array('csv', 'nids', 'hids', 'xml', 'restSearch', 'stix', 'updateGraph', 'downloadOpenIOCEvent'),
'attributes' => array('text', 'downloadAttachment', 'returnAttributes', 'restSearch', 'rpz', 'bro'),
'objects' => array('restSearch')
);
protected $_legacyParams = array();
/** @var array */
public $userRole;
@ -202,20 +195,7 @@ class AppController extends Controller
if (empty($dataToDecode)) {
return null;
}
try {
if (defined('JSON_THROW_ON_ERROR')) {
// JSON_THROW_ON_ERROR is supported since PHP 7.3
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
} else {
$decoded = json_decode($dataToDecode, true);
if ($decoded === null) {
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
}
return $decoded;
}
} catch (Exception $e) {
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
}
return $this->_jsonDecode($dataToDecode);
};
// Throw exception if JSON in request is invalid. Default CakePHP behaviour would just ignore that error.
$this->RequestHandler->addInputType('json', [$jsonDecode]);
@ -376,7 +356,7 @@ class AppController extends Controller
{
// Notifications and homepage is not necessary for AJAX or REST requests
$user = $this->Auth->user();
if ($user && !$this->_isRest() && !$this->request->is('ajax')) {
if ($user && !$this->_isRest() && isset($this->User) && !$this->request->is('ajax')) {
$hasNotifications = $this->User->hasNotifications($user);
$this->set('hasNotifications', $hasNotifications);
@ -447,6 +427,10 @@ class AppController extends Controller
);
$this->Log->save($log);
}
$storeAPITime = Configure::read('MISP.store_api_access_time');
if (!empty($storeAPITime) && $storeAPITime) {
$this->User->updateAPIAccessTime($user);
}
$this->Session->renew();
$this->Session->write(AuthComponent::$sessionKey, $user);
$this->isApiAuthed = true;
@ -541,7 +525,7 @@ class AppController extends Controller
return false;
}
if ($user['disabled']) {
if ($user['disabled'] || (isset($user['logged_by_authkey']) && $user['logged_by_authkey']) && !$this->User->checkIfUserIsValid($user)) {
if ($this->_shouldLog('disabled:' . $user['id'])) {
$this->Log = ClassRegistry::init('Log');
$this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], 'Login attempt by disabled user.');
@ -823,8 +807,8 @@ class AppController extends Controller
ConnectionManager::create('default', $db->config);
}
$dataSource = $dataSourceConfig['datasource'];
if (!in_array($dataSource, array('Database/Mysql', 'Database/Postgres', 'Database/MysqlObserver'))) {
throw new Exception('datasource not supported: ' . $dataSource);
if (!in_array($dataSource, ['Database/Mysql', 'Database/Postgres', 'Database/MysqlObserver', 'Database/MysqlExtended'], true)) {
throw new Exception('Datasource not supported: ' . $dataSource);
}
}
@ -921,11 +905,11 @@ class AppController extends Controller
/**
* generic function to standardise on the collection of parameters. Accepts posted request objects, url params, named url params
* @param array $options
* @param $exception
* @param CakeResponse $exception
* @param array $data
* @return array|false|mixed
* @return array|false
*/
protected function _harvestParameters($options, &$exception = null, $data = array())
protected function _harvestParameters($options, &$exception = null, $data = [])
{
$request = $options['request'] ?? $this->request;
if ($request->is('post')) {
@ -971,14 +955,15 @@ class AppController extends Controller
}
}
}
foreach ($data as $k => $v) {
if (!is_array($data[$k])) {
$data[$k] = trim($data[$k]);
if (strpos($data[$k], '||')) {
$data[$k] = explode('||', $data[$k]);
foreach ($data as &$v) {
if (is_string($v)) {
$v = trim($v);
if (strpos($v, '||')) {
$v = explode('||', $v);
}
}
}
unset($v);
if (!empty($options['additional_delimiters'])) {
if (!is_array($options['additional_delimiters'])) {
$options['additional_delimiters'] = array($options['additional_delimiters']);
@ -988,6 +973,7 @@ class AppController extends Controller
foreach ($options['additional_delimiters'] as $delim) {
if (strpos($v, $delim) !== false) {
$found = true;
break;
}
}
if ($found) {
@ -1270,17 +1256,17 @@ class AppController extends Controller
]);
}
}
/** @var TmpFileTool $final */
$final = $model->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView);
if (!empty($renderView) && !empty($final)) {
if ($renderView) {
$this->layout = false;
$final = json_decode($final->intoString(), true);
foreach ($final as $key => $data) {
$this->set($key, $data);
}
$this->set($final);
$this->render('/Events/module_views/' . $renderView);
} else {
$filename = $this->RestSearch->getFilename($filters, $scope, $responseType);
return $this->RestResponse->viewData($final, $responseType, false, true, $filename, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
$headers = ['X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType];
return $this->RestResponse->viewData($final, $responseType, false, true, $filename, $headers);
}
}
@ -1306,9 +1292,36 @@ class AppController extends Controller
* Returns true if user can modify given event.
*
* @param array $event
* @param array|null $user If empty, currently logged user will be used
* @return bool
*/
protected function __canModifyEvent(array $event)
protected function __canModifyEvent(array $event, $user = null)
{
if (!isset($event['Event'])) {
throw new InvalidArgumentException('Passed object does not contain an Event.');
}
$user = $user ?: $this->Auth->user();
if ($user['Role']['perm_site_admin']) {
return true;
}
if ($user['Role']['perm_modify_org'] && $event['Event']['orgc_id'] == $user['org_id']) {
return true;
}
if ($user['Role']['perm_modify'] && $event['Event']['user_id'] == $user['id']) {
return true;
}
return false;
}
/**
* Returns true if user can publish the given event.
*
* @param array $event
* @return bool
*/
protected function __canPublishEvent(array $event)
{
if (!isset($event['Event'])) {
throw new InvalidArgumentException('Passed object does not contain an Event.');
@ -1317,10 +1330,7 @@ class AppController extends Controller
if ($this->userRole['perm_site_admin']) {
return true;
}
if ($this->userRole['perm_modify_org'] && $event['Event']['orgc_id'] == $this->Auth->user()['org_id']) {
return true;
}
if ($this->userRole['perm_modify'] && $event['Event']['user_id'] == $this->Auth->user()['id']) {
if ($this->userRole['perm_publish'] && $event['Event']['orgc_id'] == $this->Auth->user()['org_id']) {
return true;
}
return false;
@ -1429,4 +1439,64 @@ class AppController extends Controller
}
return parent::_getViewObject();
}
public function getEventManager()
{
if (empty($this->_eventManager)) {
$this->_eventManager = new BetterCakeEventManager();
$this->_eventManager->attach($this->Components);
$this->_eventManager->attach($this);
}
return $this->_eventManager;
}
/**
* Close session without writing changes to them and return current user.
* @return array
*/
protected function _closeSession()
{
$user = $this->Auth->user();
session_abort();
return $user;
}
/**
* Decode JSON with proper error handling.
* @param string $dataToDecode
* @return mixed
*/
protected function _jsonDecode($dataToDecode)
{
try {
return JsonTool::decode($dataToDecode);
} catch (Exception $e) {
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
}
}
/**
* Mimics what PaginateComponent::paginate() would do, when Model::paginate() is not called
*
* @param integer $page
* @param integer $limit
* @param integer $current
* @param string $type
* @return void
*/
protected function __setPagingParams(int $page, int $limit, int $current, string $type = 'named')
{
$this->request->params['paging'] = [
'Correlation' => [
'page' => $page,
'limit' => $limit,
'current' => $current,
'pageCount' => 0,
'prevPage' => $page > 1,
'nextPage' => $current >= $limit,
'options' => [],
'paramType' => $type
]
];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,7 @@ class AuditLogsController extends AppController
'GalaxyClusterRelation',
'News',
'Warninglist',
'Workflow',
];
public $paginate = [
@ -67,9 +68,9 @@ class AuditLogsController extends AppController
],
];
public function __construct($id = false, $table = null, $ds = null)
public function __construct($request = null, $response = null)
{
parent::__construct($id, $table, $ds);
parent::__construct($request, $response);
$this->actions = [
AuditLog::ACTION_ADD => __('Add'),
AuditLog::ACTION_EDIT => __('Edit'),
@ -99,7 +100,22 @@ class AuditLogsController extends AppController
$this->paginate['fields'][] = 'request_id';
}
$this->paginate['conditions'] = $this->__searchConditions();
$params = $this->IndexFilter->harvestParameters([
'ip',
'user',
'request_id',
'authkey_id',
'model',
'model_id',
'event_id',
'model_title',
'action',
'org',
'created',
'request_type',
]);
$this->paginate['conditions'] = $this->__searchConditions($params);
$list = $this->paginate();
if ($this->_isRest()) {
@ -133,32 +149,27 @@ class AuditLogsController extends AppController
public function eventIndex($eventId, $org = null)
{
$this->loadModel('Event');
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
$event = $this->AuditLog->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
if (empty($event)) {
throw new NotFoundException('Invalid event.');
}
$this->paginate['conditions'] = $this->__createEventIndexConditions($event);
$params = $this->IndexFilter->harvestParameters(['created', 'org']);
if ($org) {
$org = $this->AuditLog->Organisation->fetchOrg($org);
if ($org) {
$this->paginate['conditions']['AND']['org_id'] = $org['id'];
} else {
$this->paginate['conditions']['AND']['org_id'] = -1;
}
$params['org'] = $org;
}
$this->paginate['conditions'][] = $this->__searchConditions($params);
$list = $this->paginate();
if (!$this->_isSiteAdmin()) {
// Remove all user info about users from different org
$this->loadModel('User');
$orgUserIds = $this->User->find('column', array(
$orgUserIds = $this->User->find('column', [
'conditions' => ['User.org_id' => $this->Auth->user('org_id')],
'fields' => ['User.id'],
));
]);
foreach ($list as $k => $item) {
if ($item['AuditLog']['user_id'] == 0) {
continue;
@ -199,15 +210,13 @@ class AuditLogsController extends AppController
public function returnDates($org = 'all')
{
if (!$this->Auth->user('Role')['perm_sharing_group'] && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
if ($org !== 'all' && $org !== $this->Auth->user('Organisation')['name']) {
$user = $this->_closeSession();
if (!$user['Role']['perm_sharing_group'] && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
if ($org !== 'all' && $org !== $user['Organisation']['name']) {
throw new MethodNotAllowedException('Invalid organisation.');
}
}
// Fetching dates can be slow, so to allow concurrent requests, we can close sessions to release session lock
session_write_close();
$data = $this->AuditLog->returnDates($org);
return $this->RestResponse->viewData($data, $this->response->type());
}
@ -215,23 +224,8 @@ class AuditLogsController extends AppController
/**
* @return array
*/
private function __searchConditions()
private function __searchConditions(array $params)
{
$params = $this->IndexFilter->harvestParameters([
'ip',
'user',
'request_id',
'authkey_id',
'model',
'model_id',
'event_id',
'model_title',
'action',
'org',
'created',
'request_type',
]);
$qbRules = [];
foreach ($params as $key => $value) {
if ($key === 'model' && strpos($value, ':') !== false) {
@ -351,7 +345,7 @@ class AuditLogsController extends AppController
return ['event_id' => $event['Event']['id']];
}
$event = $this->Event->fetchEvent($this->Auth->user(), [
$event = $this->AuditLog->Event->fetchEvent($this->Auth->user(), [
'eventid' => $event['Event']['id'],
'sgReferenceOnly' => 1,
'deleted' => [0, 1],

View File

@ -21,10 +21,40 @@ class AuthKeysController extends AppController
public function index($id = false)
{
$conditions = $this->__prepareConditions();
if ($id !== false) {
$canCreateAuthkey = true;
if ($id) {
$this->set('user_id', $id);
if ($this->_isAdmin()) {
if ($this->_isSiteAdmin()) {
$canCreateAuthkey = true;
} else {
$user = $this->AuthKey->User->find('first', [
'recursive' => -1,
'conditions' => [
'User.id' => $id,
'User.disabled' => false
],
'fields' => ['User.id', 'User.org_id', 'User.disabled'],
'contain' => [
'Role' => [
'fields' => [
'Role.perm_site_admin', 'Role.perm_admin'
]
]
]
]);
if ($user['Role']['perm_site_admin'] || ($user['Role']['perm_admin'] && $user['User']['id'] !== $this->Auth->user('id'))) {
$canCreateAuthkey = false;
} else {
$canCreateAuthkey = true;
}
}
} else {
$canCreateAuthkey = (int)$id === (int)$this->Auth->user('id');
}
$conditions['AND'][] = ['AuthKey.user_id' => $id];
}
$this->set('canCreateAuthkey', $canCreateAuthkey);
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys');
$this->CRUD->index([
'filters' => ['User.email', 'authkey_start', 'authkey_end', 'comment', 'User.id'],
@ -105,19 +135,58 @@ class AuthKeysController extends AppController
public function add($user_id = false)
{
$options = $this->IndexFilter->harvestParameters(['user_id']);
if (!empty($params['user_id'])) {
$user_id = $options['user_id'];
}
$params = [
'displayOnSuccess' => 'authkey_display',
'saveModelVariable' => ['authkey_raw'],
'override' => ['authkey' => null], // do not allow to use own key, always generate random one
'afterFind' => function ($authKey) { // remove hashed key from response
'afterFind' => function (array $authKey, array $savedData) { // remove hashed key from response
unset($authKey['AuthKey']['authkey']);
$authKey['AuthKey']['authkey_raw'] = $savedData['AuthKey']['authkey_raw'];
return $authKey;
}
];
if ($user_id === 'me' || $user_id === false) {
$user_id = $this->Auth->user('id');
}
$selectConditions = [];
if (!$this->_isSiteAdmin()) {
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
$params['override']['user_id'] = $this->Auth->user('id');
if ($this->_isAdmin()) {
$role_ids = $this->AuthKey->User->Role->find('column', [
'fields' => ['Role.id'],
'conditions' => [
'AND' => [
'Role.perm_site_admin' => false,
'Role.perm_auth' => true,
'Role.perm_admin' => false
]
]
]);
$user_ids = $this->AuthKey->User->find('column', [
'fields' => ['User.id'],
'conditions' => [
'User.org_id' => $this->Auth->user('org_id'),
'OR' => [
'User.role_id' => $role_ids,
'User.id' => $this->Auth->user('id')
]
]
]);
if (!empty($user_id)) {
if (in_array($user_id, $user_ids)) {
$user_ids = [$user_id];
} else {
throw new MethodNotAllowedException(__('Invalid user or insufficient privileges to create an authkey for the given user.'));
}
}
$selectConditions['AND'][] = ['User.id' => $user_ids];
$params['override']['user_id'] = $user_ids[0];
} else {
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
$params['override']['user_id'] = $this->Auth->user('id');
}
} else if ($user_id) {
$selectConditions['AND'][] = ['User.id' => $user_id];
$params['override']['user_id'] = $user_id;
@ -126,11 +195,10 @@ class AuthKeysController extends AppController
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->loadModel('User');
$dropdownData = [
'user' => $this->User->find('list', [
'user' => $this->AuthKey->User->find('list', [
'sort' => ['username' => 'asc'],
'conditions' => $selectConditions
'conditions' => $selectConditions,
])
];
$this->set(compact('dropdownData'));

View File

@ -125,7 +125,7 @@ class CerebratesController extends AppController
$this->set('title', __('Sync organisation information'));
$this->set('question', __('Are you sure you want to download and add / update the remote organisations from the Cerebrate node?'));
$this->set('actionName', __('Pull all'));
$this->layout = 'ajax';
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}
@ -165,7 +165,7 @@ class CerebratesController extends AppController
$this->set('title', __('Sync sharing group information'));
$this->set('question', __('Are you sure you want to download and add / update the remote sharing group from the Cerebrate node?'));
$this->set('actionName', __('Pull all'));
$this->layout = 'ajax';
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}
@ -237,7 +237,7 @@ class CerebratesController extends AppController
$this->set('title', __('Download organisation information'));
$this->set('question', __('Are you sure you want to download and add / update the remote organisation?'));
$this->set('actionName', __('Download'));
$this->layout = 'ajax';
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}
@ -309,7 +309,7 @@ class CerebratesController extends AppController
$this->set('title', __('Download sharing group information'));
$this->set('question', __('Are you sure you want to download and add / update the remote sharing group?'));
$this->set('actionName', __('Download'));
$this->layout = 'ajax';
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}

View File

@ -14,52 +14,59 @@ class ACLComponent extends Component
// If we add any new functionality to MISP and we don't add it to this list, it will only be visible to site admins.
const ACL_LIST = array(
'*' => array(
'blackhole' => array(),
'debugACL' => array(),
'queryACL' => array(),
'restSearch' => array('*'),
'blackhole' => array(),
'debugACL' => array(),
'queryACL' => array(),
'restSearch' => array('*'),
),
'api' => [
'rest' => ['perm_auth'],
'viewDeprecatedFunctionUse' => [],
'openapi' => ['*'],
'getApiInfo' => ['*'],
'getAllApis' => ['*'],
],
'attributes' => array(
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'add_threatconnect' => array('perm_add'),
'addTag' => array('perm_tagger'),
'attributeReplace' => array('perm_add'),
'attributeStatistics' => array('*'),
'bro' => array('*'),
'checkAttachments' => array(),
'checkComposites' => array('perm_admin'),
'checkOrphanedAttributes' => array(),
'delete' => array('perm_add'),
'deleteSelected' => array('perm_add'),
'describeTypes' => array('*'),
'download' => array('*'),
'downloadAttachment' => array('*'),
'downloadSample' => array('*'),
'edit' => array('perm_add'),
'editField' => array('perm_add'),
'editSelected' => array('perm_add'),
'exportSearch' => array('*'),
'fetchEditForm' => array('perm_add'),
'fetchViewValue' => array('*'),
'generateCorrelation' => array(),
'getMassEditForm' => array('perm_add'),
'hoverEnrichment' => array('perm_add'),
'index' => array('*'),
'pruneOrphanedAttributes' => array(),
'removeTag' => array('perm_tagger'),
'reportValidationIssuesAttributes' => array(),
'restore' => array('perm_add'),
'restSearch' => array('*'),
'returnAttributes' => array('*'),
'rpz' => array('*'),
'search' => array('*'),
'toggleCorrelation' => array('perm_add'),
'text' => array('*'),
'toggleToIDS' => array('perm_add'),
'updateAttributeValues' => array('perm_add'),
'view' => array('*'),
'viewPicture' => array('*'),
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'add_threatconnect' => array('perm_add'),
'addTag' => array('perm_tagger'),
'attributeReplace' => array('perm_add'),
'attributeStatistics' => array('*'),
'bro' => array('*'),
'checkAttachments' => array(),
'checkComposites' => array('perm_admin'),
'checkOrphanedAttributes' => array(),
'delete' => array('perm_add'),
'deleteSelected' => array('perm_add'),
'describeTypes' => array('*'),
'download' => array('*'),
'downloadAttachment' => array('*'),
'downloadSample' => array('*'),
'edit' => array('perm_add'),
'editField' => array('perm_add'),
'editSelected' => array('perm_add'),
'exportSearch' => array('*'),
'fetchEditForm' => array('perm_add'),
'fetchViewValue' => array('*'),
'generateCorrelation' => array(),
'getMassEditForm' => array('perm_add'),
'hoverEnrichment' => array('perm_add'),
'index' => array('*'),
'pruneOrphanedAttributes' => array(),
'removeTag' => array('perm_tagger'),
'reportValidationIssuesAttributes' => array(),
'restore' => array('perm_add'),
'restSearch' => array('*'),
'returnAttributes' => array('*'),
'rpz' => array('*'),
'search' => array('*'),
'toggleCorrelation' => array('perm_add'),
'text' => array('*'),
'toggleToIDS' => array('perm_add'),
'updateAttributeValues' => array('perm_add'),
'view' => array('*'),
'viewPicture' => array('*'),
),
'authKeys' => [
'add' => ['AND' => ['perm_auth', 'not_read_only_authkey']],
@ -90,8 +97,18 @@ class ACLComponent extends Component
'view' => []
],
'correlations' => [
'generateOccurrences' => [],
'generateTopCorrelations' => [],
'top' => []
'overCorrelations' => [],
'switchEngine' => [],
'top' => [],
'truncate' => []
],
'cryptographicKeys' => [
'add' => ['perm_add'],
'delete' => ['perm_add'],
'index' => ['*'],
'view' => ['*']
],
'dashboards' => array(
'getForm' => array('*'),
@ -128,41 +145,41 @@ class ACLComponent extends Component
"linkAttributeTypeToModel" => array( 'OR' => array('perm_admin', 'perm_decaying'))
),
'communities' => array(
'index' => array(),
'requestAccess' => array(),
'view' => array()
'index' => array(),
'requestAccess' => array(),
'view' => array()
),
'eventBlocklists' => array(
'add' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'delete' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'edit' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'index' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'massDelete' => [
'AND' => [
'host_org_user',
'perm_add'
]
'add' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'delete' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'edit' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'index' => [
'AND' => [
'host_org_user',
'perm_add'
]
],
'massDelete' => [
'AND' => [
'host_org_user',
'perm_add'
]
]
),
'eventDelegations' => array(
'acceptDelegation' => array('AND' => ['delegation_enabled', 'perm_add']),
@ -219,7 +236,6 @@ class ACLComponent extends Component
'getEventGraphTags' => array('*'),
'getEventGraphGeneric' => array('*'),
'getEventTimeline' => array('*'),
'genDistributionGraph' => array('*'),
'getDistributionGraph' => array('*'),
'getReferenceData' => array('*'),
'getReferences' => array('*'),
@ -232,7 +248,9 @@ class ACLComponent extends Component
'massDelete' => array(),
'merge' => array('perm_modify'),
'nids' => array('*'),
'populate' => array('perm_add'),
'proposalEventIndex' => array('*'),
'protect' => ['perm_add'],
'publish' => array('perm_publish'),
'publishSightings' => array('perm_sighting'),
'pushEventToZMQ' => array('perm_publish_zmq'),
@ -245,12 +263,14 @@ class ACLComponent extends Component
'reportValidationIssuesEvents' => array(),
'restoreDeletedEvents' => array(),
'restSearch' => array('*'),
'restSearchExport' => array('*'),
'runTaxonomyExclusivityCheck' => array('*'),
'saveFreeText' => array('perm_add'),
'stix' => array('*'),
'stix2' => array('*'),
'strposarray' => array(),
'toggleCorrelation' => array('perm_add'),
'unprotect' => ['perm_add'],
'unpublish' => array('perm_modify'),
'updateGraph' => array('*'),
'upload_analysis_file' => array('perm_add'),
@ -323,7 +343,6 @@ class ACLComponent extends Component
),
'galaxyClusters' => array(
'add' => array('perm_galaxy_editor'),
'attachToEvent' => array('perm_tagger'),
'delete' => array('perm_galaxy_editor'),
'detach' => array('perm_tagger'),
'edit' => array('perm_galaxy_editor'),
@ -434,295 +453,308 @@ class ACLComponent extends Component
'viewElements' => array('*')
),
'orgBlocklists' => array(
'add' => array(),
'delete' => array(),
'edit' => array(),
'index' => array(),
'add' => array(),
'delete' => array(),
'edit' => array(),
'index' => array(),
),
'organisations' => array(
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_generateuuid' => array(),
'admin_merge' => array(),
'fetchOrgsForSG' => array('perm_sharing_group'),
'fetchSGOrgRow' => array('*'),
'getUUIDs' => array('perm_sync'),
'index' => array('*'),
'view' => array('*'),
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_generateuuid' => array(),
'admin_merge' => array(),
'fetchOrgsForSG' => array('perm_sharing_group'),
'fetchSGOrgRow' => array('*'),
'getUUIDs' => array('perm_sync'),
'index' => array('*'),
'view' => array('*'),
),
'pages' => array(
'display' => array('*'),
'display' => array('*'),
),
'posts' => array(
'add' => array('not_read_only_authkey'),
'delete' => array('not_read_only_authkey'),
'edit' => array('not_read_only_authkey'),
'pushMessageToZMQ' => array()
'add' => array('not_read_only_authkey'),
'delete' => array('not_read_only_authkey'),
'edit' => array('not_read_only_authkey'),
'pushMessageToZMQ' => array()
),
'regexp' => array(
'admin_add' => array('perm_regexp_access'),
'admin_clean' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'cleanRegexModifiers' => array('perm_regexp_access'),
'index' => array('*'),
'admin_add' => array('perm_regexp_access'),
'admin_clean' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'cleanRegexModifiers' => array('perm_regexp_access'),
'index' => array('*'),
),
'restClientHistory' => array(
'delete' => array('not_read_only_authkey'),
'index' => array('*')
'delete' => array('not_read_only_authkey'),
'index' => array('*')
),
'roles' => array(
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_set_default' => array(),
'index' => array('*'),
'view' => array('*'),
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_set_default' => array(),
'index' => array('*'),
'view' => array('*'),
),
'servers' => array(
'add' => array(),
'dbSchemaDiagnostic' => array(),
'cache' => array(),
'changePriority' => array(),
'checkout' => array(),
'clearWorkerQueue' => array(),
'createSync' => array('perm_sync'),
'delete' => array(),
'deleteFile' => array(),
'edit' => array(),
'eventBlockRule' => array(),
'fetchServersForSG' => array('perm_sharing_group'),
'filterEventIndex' => array(),
'getApiInfo' => array('*'),
'getAvailableSyncFilteringRules' => array('*'),
'getInstanceUUID' => array('perm_sync'),
'getPyMISPVersion' => array('*'),
'getRemoteUser' => array(),
'getSetting' => array(),
'getSubmodulesStatus' => array(),
'getSubmoduleQuickUpdateForm' => array(),
'getWorkers' => array(),
'getVersion' => array('perm_auth'),
'idTranslator' => ['host_org_user'],
'import' => array(),
'index' => array(),
'ondemandAction' => array(),
'postTest' => array('*'),
'previewEvent' => array(),
'previewIndex' => array(),
'compareServers' => [],
'pull' => array(),
'purgeSessions' => array(),
'push' => array(),
'queryAvailableSyncFilteringRules' => array(),
'releaseUpdateLock' => array(),
'resetRemoteAuthKey' => array(),
'removeOrphanedCorrelations' => array(),
'rest' => array('perm_auth'),
'openapi' => array('*'),
'restartDeadWorkers' => array(),
'restartWorkers' => array(),
'serverSettings' => array(),
'serverSettingsEdit' => array(),
'serverSettingsReloadSetting' => array(),
'startWorker' => array(),
'startZeroMQServer' => array(),
'statusZeroMQServer' => array(),
'stopWorker' => array(),
'stopZeroMQServer' => array(),
'testConnection' => array(),
'update' => array(),
'updateJSON' => array(),
'updateProgress' => array(),
'updateSubmodule' => array(),
'uploadFile' => array(),
'viewDeprecatedFunctionUse' => array(),
'killAllWorkers' => [],
'add' => array(),
'dbSchemaDiagnostic' => array(),
'dbConfiguration' => array(),
'cache' => array(),
'changePriority' => array(),
'checkout' => array(),
'clearWorkerQueue' => array(),
'createSync' => array('perm_sync'),
'delete' => array(),
'deleteFile' => array(),
'edit' => array(),
'eventBlockRule' => array(),
'fetchServersForSG' => array('perm_sharing_group'),
'filterEventIndex' => array(),
'getAvailableSyncFilteringRules' => array('*'),
'getInstanceUUID' => array('perm_sync'),
'getPyMISPVersion' => array('*'),
'getRemoteUser' => array(),
'getSetting' => array(),
'getSubmodulesStatus' => array(),
'getSubmoduleQuickUpdateForm' => array(),
'getWorkers' => array(),
'getVersion' => array('perm_auth'),
'idTranslator' => ['host_org_user'],
'import' => array(),
'index' => array(),
'ipUser' => ['perm_site_admin'],
'ondemandAction' => array(),
'postTest' => array('*'),
'previewEvent' => array(),
'previewIndex' => array(),
'compareServers' => [],
'pull' => array(),
'purgeSessions' => array(),
'push' => array(),
'queryAvailableSyncFilteringRules' => array(),
'releaseUpdateLock' => array(),
'resetRemoteAuthKey' => array(),
'removeOrphanedCorrelations' => array(),
'restartDeadWorkers' => array(),
'restartWorkers' => array(),
'serverSettings' => array(),
'serverSettingsEdit' => array(),
'serverSettingsReloadSetting' => array(),
'startWorker' => array(),
'startZeroMQServer' => array(),
'statusZeroMQServer' => array(),
'stopWorker' => array(),
'stopZeroMQServer' => array(),
'testConnection' => array(),
'update' => array(),
'updateJSON' => array(),
'updateProgress' => array(),
'updateSubmodule' => array(),
'uploadFile' => array(),
'killAllWorkers' => [],
'cspReport' => ['*'],
'pruneDuplicateUUIDs' => array(),
'removeDuplicateEvents' => array(),
'upgrade2324' => array(),
'cleanModelCaches' => array(),
'updateDatabase' => array(),
'rest' => ['perm_auth'],
),
'shadowAttributes' => array(
'accept' => array('perm_add'),
'acceptSelected' => array('perm_add'),
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'delete' => array('perm_add'),
'discard' => array('perm_add'),
'discardSelected' => array('perm_add'),
'download' => array('*'),
'edit' => array('perm_add'),
'generateCorrelation' => array(),
'index' => array('*'),
'view' => array('*'),
'viewPicture' => array('*'),
'accept' => array('perm_add'),
'acceptSelected' => array('perm_add'),
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'delete' => array('perm_add'),
'discard' => array('perm_add'),
'discardSelected' => array('perm_add'),
'download' => array('*'),
'edit' => array('perm_add'),
'generateCorrelation' => array(),
'index' => array('*'),
'view' => array('*'),
'viewPicture' => array('*'),
),
'sharingGroupBlueprints' => array(
'add' => array('perm_sharing_group'),
'delete' => array('perm_sharing_group'),
'detach' => array('perm_sharing_group'),
'edit' => array('perm_sharing_group'),
'execute' => array('perm_sharing_group'),
'index' => array('perm_sharing_group'),
'view' => array('perm_sharing_group'),
'viewOrgs' => array('perm_sharing_group'),
),
'sharingGroups' => array(
'add' => array('perm_sharing_group'),
'addServer' => array('perm_sharing_group'),
'addOrg' => array('perm_sharing_group'),
'delete' => array('perm_sharing_group'),
'edit' => array('perm_sharing_group'),
'index' => array('*'),
'removeServer' => array('perm_sharing_group'),
'removeOrg' => array('perm_sharing_group'),
'view' => array('*'),
'add' => array('perm_sharing_group'),
'addServer' => array('perm_sharing_group'),
'addOrg' => array('perm_sharing_group'),
'delete' => array('perm_sharing_group'),
'edit' => array('perm_sharing_group'),
'index' => array('*'),
'removeServer' => array('perm_sharing_group'),
'removeOrg' => array('perm_sharing_group'),
'view' => array('*'),
),
'sightings' => array(
'add' => array('perm_sighting'),
'restSearch' => array('perm_sighting'),
'advanced' => array('perm_sighting'),
'delete' => array('perm_sighting'),
'index' => array('*'),
'listSightings' => array('*'),
'quickDelete' => array('perm_sighting'),
'viewSightings' => array('*'),
'bulkSaveSightings' => array('OR' => array('perm_sync', 'perm_sighting')),
'filterSightingUuidsForPush' => ['perm_sync'],
'quickAdd' => array('perm_sighting')
'add' => array('perm_sighting'),
'restSearch' => array('perm_sighting'),
'advanced' => array('perm_sighting'),
'delete' => array('perm_sighting'),
'index' => array('*'),
'listSightings' => array('*'),
'quickDelete' => array('perm_sighting'),
'viewSightings' => array('*'),
'bulkSaveSightings' => array('OR' => array('perm_sync', 'perm_sighting')),
'filterSightingUuidsForPush' => ['perm_sync'],
'quickAdd' => array('perm_sighting')
),
'sightingdb' => array(
'add' => array(),
'edit' => array(),
'delete' => array(),
'index' => array(),
'requestStatus' => array(),
'search' => array()
'add' => array(),
'edit' => array(),
'delete' => array(),
'index' => array(),
'requestStatus' => array(),
'search' => array()
),
'tagCollections' => array(
'add' => array('perm_tag_editor'),
'addTag' => array('perm_tag_editor'),
'delete' => array('perm_tag_editor'),
'edit' => array('perm_tag_editor'),
'getRow' => array('perm_tag_editor'),
'import' => array('perm_tag_editor'),
'index' => array('*'),
'removeTag' => array('perm_tag_editor'),
'view' => array('*')
'add' => array('perm_tag_editor'),
'addTag' => array('perm_tag_editor'),
'delete' => array('perm_tag_editor'),
'edit' => array('perm_tag_editor'),
'getRow' => array('perm_tag_editor'),
'import' => array('perm_tag_editor'),
'index' => array('*'),
'removeTag' => array('perm_tag_editor'),
'view' => array('*')
),
'tags' => array(
'add' => array('perm_tag_editor'),
'attachTagToObject' => array('perm_tagger'),
'delete' => array(),
'edit' => array(),
'index' => array('*'),
'quickAdd' => array('perm_tag_editor'),
'removeTagFromObject' => array('perm_tagger'),
'search' => array('*'),
'selectTag' => array('perm_tagger'),
'selectTaxonomy' => array('perm_tagger'),
'showEventTag' => array('*'),
'showAttributeTag' => array('*'),
'showTagControllerTag' => array('*'),
'tagStatistics' => array('*'),
'view' => array('*'),
'viewGraph' => array('*'),
'viewTag' => array('*')
'add' => array('perm_tag_editor'),
'attachTagToObject' => array('perm_tagger'),
'delete' => array(),
'edit' => array(),
'index' => array('*'),
'quickAdd' => array('perm_tag_editor'),
'removeTagFromObject' => array('perm_tagger'),
'search' => array('*'),
'selectTag' => array('perm_tagger'),
'selectTaxonomy' => array('perm_tagger'),
'showEventTag' => array('*'),
'showAttributeTag' => array('*'),
'showTagControllerTag' => array('*'),
'tagStatistics' => array('*'),
'view' => array('*'),
'viewGraph' => array('*'),
'viewTag' => array('*')
),
'tasks' => array(
'index' => array(),
'setTask' => array(),
'index' => array(),
'setTask' => array(),
),
'taxonomies' => array(
'addTag' => array(),
'delete' => array(),
'disable' => array(),
'disableTag' => array(),
'enable' => array(),
'index' => array('*'),
'taxonomy_tags' => array('*'),
'taxonomyMassConfirmation' => array('perm_tagger'),
'taxonomyMassHide' => array('perm_tagger'),
'taxonomyMassUnhide' => array('perm_tagger'),
'toggleRequired' => array(),
'update' => array(),
'import' => [],
'export' => ['*'],
'view' => array('*'),
'unhideTag' => array('perm_tagger'),
'hideTag' => array('perm_tagger'),
'addTag' => array(),
'delete' => array(),
'disable' => array(),
'disableTag' => array(),
'enable' => array(),
'index' => array('*'),
'taxonomy_tags' => array('*'),
'taxonomyMassConfirmation' => array('perm_tagger'),
'taxonomyMassHide' => array('perm_tagger'),
'taxonomyMassUnhide' => array('perm_tagger'),
'toggleRequired' => array(),
'update' => array(),
'import' => [],
'export' => ['*'],
'view' => array('*'),
'unhideTag' => array('perm_tagger'),
'hideTag' => array('perm_tagger'),
'normalizeCustomTagsToTaxonomyFormat' => [],
),
'templateElements' => array(
'add' => array('perm_template'),
'delete' => array('perm_template'),
'edit' => array('perm_template'),
'index' => array('*'),
'templateElementAddChoices' => array('perm_template'),
'add' => array('perm_template'),
'delete' => array('perm_template'),
'edit' => array('perm_template'),
'index' => array('*'),
'templateElementAddChoices' => array('perm_template'),
),
'templates' => array(
'add' => array('perm_template'),
'delete' => array('perm_template'),
'deleteTemporaryFile' => array('perm_add'),
'edit' => array('perm_template'),
'index' => array('*'),
'populateEventFromTemplate' => array('perm_add'),
'saveElementSorting' => array('perm_template'),
'submitEventPopulation' => array('perm_add'),
'templateChoices' => array('*'),
'uploadFile' => array('*'),
'view' => array('*'),
'add' => array('perm_template'),
'delete' => array('perm_template'),
'deleteTemporaryFile' => array('perm_add'),
'edit' => array('perm_template'),
'index' => array('*'),
'populateEventFromTemplate' => array('perm_add'),
'saveElementSorting' => array('perm_template'),
'submitEventPopulation' => array('perm_add'),
'templateChoices' => array('*'),
'uploadFile' => array('*'),
'view' => array('*'),
),
'threads' => array(
'index' => array('*'),
'view' => array('*'),
'viewEvent' => array('*'),
'index' => array('*'),
'view' => array('*'),
'viewEvent' => array('*'),
),
'users' => array(
'acceptRegistrations' => array(),
'admin_add' => ['AND' => ['perm_admin', 'add_user_enabled']],
'admin_delete' => array('perm_admin'),
'admin_edit' => array('perm_admin'),
'admin_email' => array('perm_admin'),
'admin_filterUserIndex' => array('perm_admin'),
'admin_index' => array('perm_admin'),
'admin_massToggleField' => array('perm_admin'),
'admin_monitor' => array(),
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),
'attributehistogram' => array('*'),
'change_pw' => ['AND' => ['self_management_enabled', 'password_change_enabled', 'not_read_only_authkey']],
'checkAndCorrectPgps' => array(),
'checkIfLoggedIn' => array('*'),
'dashboard' => array('*'),
'delete' => array('perm_admin'),
'discardRegistrations' => array(),
'downloadTerms' => array('*'),
'edit' => array('self_management_enabled'),
'email_otp' => array('*'),
'searchGpgKey' => array('*'),
'fetchGpgKey' => array('*'),
'histogram' => array('*'),
'initiatePasswordReset' => ['AND' => ['perm_admin', 'password_change_enabled']],
'login' => array('*'),
'logout' => array('*'),
'register' => array('*'),
'registrations' => array(),
'resetAllSyncAuthKeys' => array(),
'resetauthkey' => ['AND' => ['self_management_enabled', 'perm_auth', 'not_read_only_authkey']],
'request_API' => array('*'),
'routeafterlogin' => array('*'),
'statistics' => array('*'),
'tagStatisticsGraph' => array('*'),
'terms' => array('*'),
'updateLoginTime' => array('*'),
'updateToAdvancedAuthKeys' => array(),
'verifyCertificate' => array(),
'verifyGPG' => array(),
'view' => array('*'),
'getGpgPublicKey' => array('*'),
'acceptRegistrations' => array(),
'admin_add' => ['AND' => ['perm_admin', 'add_user_enabled']],
'admin_delete' => array('perm_admin'),
'admin_edit' => array('perm_admin'),
'admin_email' => array('perm_admin'),
'admin_filterUserIndex' => array('perm_admin'),
'admin_index' => array('perm_admin'),
'admin_massToggleField' => array('perm_admin'),
'admin_monitor' => array(),
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),
'attributehistogram' => array('*'),
'change_pw' => ['AND' => ['self_management_enabled', 'password_change_enabled', 'not_read_only_authkey']],
'checkAndCorrectPgps' => array(),
'checkIfLoggedIn' => array('*'),
'dashboard' => array('*'),
'delete' => array('perm_admin'),
'discardRegistrations' => array(),
'downloadTerms' => array('*'),
'edit' => array('self_management_enabled'),
'email_otp' => array('*'),
'searchGpgKey' => array('*'),
'fetchGpgKey' => array('*'),
'histogram' => array('*'),
'initiatePasswordReset' => ['AND' => ['perm_admin', 'password_change_enabled']],
'login' => array('*'),
'logout' => array('*'),
'notificationSettings' => ['*'],
'register' => array('*'),
'registrations' => array(),
'resetAllSyncAuthKeys' => array(),
'resetauthkey' => ['AND' => ['self_management_enabled', 'perm_auth', 'not_read_only_authkey']],
'request_API' => array('*'),
'routeafterlogin' => array('*'),
'statistics' => array('*'),
'tagStatisticsGraph' => array('*'),
'terms' => array('*'),
'updateLoginTime' => array('*'),
'updateToAdvancedAuthKeys' => array(),
'verifyCertificate' => array(),
'verifyGPG' => array(),
'view' => array('*'),
'viewPeriodicSummary' => ['*'],
'getGpgPublicKey' => array('*'),
'unsubscribe' => ['*'],
),
'userSettings' => array(
'index' => array('*'),
'view' => array('*'),
'setSetting' => array('not_read_only_authkey'),
'getSetting' => array('*'),
'delete' => array('not_read_only_authkey'),
'setHomePage' => array('not_read_only_authkey'),
'index' => array('*'),
'view' => array('*'),
'setSetting' => array('not_read_only_authkey'),
'getSetting' => array('*'),
'delete' => array('not_read_only_authkey'),
'setHomePage' => array('not_read_only_authkey'),
'eventIndexColumnToggle' => ['*'],
),
'warninglists' => array(
@ -739,18 +771,45 @@ class ACLComponent extends Component
'export' => ['*'],
'import' => ['perm_warninglist'],
),
'workflows' => [
'index'=> [],
'rebuildRedis'=> [],
'edit'=> [],
'delete'=> [],
'view'=> [],
'editor'=> [],
'triggers'=> [],
'moduleIndex'=> [],
'moduleView'=> [],
'toggleModule'=> [],
'checkGraph'=> [],
'executeWorkflow'=> [],
'debugToggleField'=> [],
'massToggleField'=> [],
'moduleStatelessExecution'=> [],
],
'workflowBlueprints' => [
'add' => [],
'delete' => [],
'edit' => [],
'export' => [],
'import' => [],
'index' => [],
'update' => [],
'view' => [],
],
'allowedlists' => array(
'admin_add' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'index' => array('*'),
'admin_add' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'index' => array('*'),
),
'eventGraph' => array(
'view' => array('*'),
'viewPicture' => array('*'),
'add' => array('perm_add'),
'delete' => array('perm_modify'),
'view' => array('*'),
'viewPicture' => array('*'),
'add' => array('perm_add'),
'delete' => array('perm_modify'),
)
);
@ -956,6 +1015,8 @@ class ACLComponent extends Component
private function __findAllFunctions()
{
$functionsToIgnore = ['beforeFilter', 'afterFilter', 'beforeRender', 'getEventManager'];
$functionFinder = '/function[\s\n]+(\S+)[\s\n]*\(/';
$dir = new Folder(APP . 'Controller');
$files = $dir->find('.*\.php');
@ -966,11 +1027,11 @@ class ACLComponent extends Component
$controllerName = '*';
}
$functionArray = array();
$fileContents = file_get_contents(APP . 'Controller' . DS . $file);
$fileContents = FileAccessTool::readFromFile(APP . 'Controller' . DS . $file);
$fileContents = preg_replace('/\/\*[^\*]+?\*\//', '', $fileContents);
preg_match_all($functionFinder, $fileContents, $functionArray);
foreach ($functionArray[1] as $function) {
if ($function[0] !== '_' && $function !== 'beforeFilter' && $function !== 'afterFilter' && $function !== 'beforeRender') {
if ($function[0] !== '_' && !in_array($function, $functionsToIgnore, true)) {
$results[$controllerName][] = $function;
}
}
@ -991,8 +1052,7 @@ class ACLComponent extends Component
$missing = array();
foreach ($results as $controller => $functions) {
foreach ($functions as $function) {
if (!isset(self::ACL_LIST[$controller])
|| !in_array($function, array_keys(self::ACL_LIST[$controller]))) {
if (!isset(self::ACL_LIST[$controller]) || !in_array($function, array_keys(self::ACL_LIST[$controller]))) {
$missing[$controller][] = $function;
}
}

View File

@ -1,8 +1,21 @@
<?php
App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
class BlowfishConstantPasswordHasher extends BlowfishPasswordHasher
class BlowfishConstantPasswordHasher extends AbstractPasswordHasher
{
/**
* @param string $password
* @return string
*/
public function hash($password)
{
$hash = password_hash($password, PASSWORD_BCRYPT);
if ($hash === false) {
throw new RuntimeException('Could not generate hashed password');
}
return $hash;
}
/**
* @param string $password
* @param string $hashedPassword
@ -10,6 +23,6 @@ class BlowfishConstantPasswordHasher extends BlowfishPasswordHasher
*/
public function check($password, $hashedPassword)
{
return hash_equals($hashedPassword, Security::hash($password, 'blowfish', $hashedPassword));
return password_verify($password, $hashedPassword);
}
}

View File

@ -90,7 +90,8 @@ class CRUDComponent extends Component
}
/** @var Model $model */
$model = $this->Controller->{$modelName};
if ($model->save($data)) {
$savedData = $model->save($data);
if ($savedData) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
@ -100,15 +101,11 @@ class CRUDComponent extends Component
'id' => $model->id
]
]);
if (!empty($params['saveModelVariable'])) {
foreach ($params['saveModelVariable'] as $var) {
if (isset($model->$var)) {
$data[$modelName][$var] = $model->$var;
}
}
if (empty($data)) {
throw new Exception("Something went wrong, saved data not found in database.");
}
if (isset($params['afterFind'])) {
$data = $params['afterFind']($data);
$data = $params['afterFind']($data, $savedData);
}
$message = __('%s added.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
@ -287,6 +284,12 @@ class CRUDComponent extends Component
}
}
}
if (isset($params['beforeDelete'])) {
$data = $params['beforeDelete']($data);
if (empty($data)) {
throw new MethodNotAllowedException('Something went wrong, delete action failed.');
}
}
if ($validationError === null && $this->Controller->request->is('post') || $this->Controller->request->is('delete')) {
if (!empty($params['modelFunction'])) {
$result = $this->Controller->$modelName->{$params['modelFunction']}($id);

View File

@ -10,27 +10,37 @@ class IndexFilterComponent extends Component
public $Controller;
public $isRest = null;
public function initialize(Controller $controller) {
// Used for isApiFunction(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
// This is used to allow authentication via headers for methods not covered by _isRest() - as that only checks for JSON and XML formats
const AUTOMATION_ARRAY = array(
'events' => array('csv', 'nids', 'hids', 'xml', 'restSearch', 'stix', 'updateGraph', 'downloadOpenIOCEvent'),
'attributes' => array('text', 'downloadAttachment', 'returnAttributes', 'restSearch', 'rpz', 'bro'),
'objects' => array('restSearch'),
);
public function initialize(Controller $controller)
{
$this->Controller = $controller;
}
// generic function to standardise on the collection of parameters. Accepts posted request objects, url params, named url params
public function harvestParameters($paramArray, &$exception = array())
public function harvestParameters($paramArray, &$exception = [])
{
$data = array();
if (!empty($this->Controller->request->is('post'))) {
if (empty($this->Controller->request->data)) {
$request = $this->Controller->request;
$data = [];
if ($request->is('post')) {
if (empty($request->data)) {
$exception = $this->Controller->RestResponse->throwException(
400,
__('Either specify the search terms in the url, or POST a json with the filter parameters.'),
'/' . $this->Controller->request->params['controller'] . '/' . $this->Controller->action
'/' . $request->params['controller'] . '/' . $this->Controller->action
);
return false;
} else {
if (isset($this->Controller->request->data['request'])) {
$data = $this->Controller->request->data['request'];
if (isset($request->data['request'])) {
$data = $request->data['request'];
} else {
$data = $this->Controller->request->data;
$data = $request->data;
}
}
}
@ -43,19 +53,20 @@ class IndexFilterComponent extends Component
$data[$p] = $options['ordered_url_params'][$p];
$data[$p] = str_replace(';', ':', $data[$p]);
}
if (isset($this->Controller->params['named'][$p])) {
$data[$p] = str_replace(';', ':', $this->Controller->params['named'][$p]);
if (isset($request->params['named'][$p])) {
$data[$p] = str_replace(';', ':', $request->params['named'][$p]);
}
}
}
foreach ($data as $k => $v) {
if (!is_array($data[$k])) {
$data[$k] = trim($data[$k]);
if (strpos($data[$k], '||')) {
$data[$k] = explode('||', $data[$k]);
foreach ($data as &$v) {
if (is_string($v)) {
$v = trim($v);
if (strpos($v, '||')) {
$v = explode('||', $v);
}
}
}
unset($v);
if (!empty($options['additional_delimiters'])) {
if (!is_array($options['additional_delimiters'])) {
$options['additional_delimiters'] = array($options['additional_delimiters']);
@ -65,6 +76,7 @@ class IndexFilterComponent extends Component
foreach ($options['additional_delimiters'] as $delim) {
if (strpos($v, $delim) !== false) {
$found = true;
break;
}
}
if ($found) {
@ -117,6 +129,6 @@ class IndexFilterComponent extends Component
*/
public function isApiFunction($controller, $action)
{
return isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller], true);
return isset(self::AUTOMATION_ARRAY[$controller]) && in_array($action, self::AUTOMATION_ARRAY[$controller], true);
}
}

View File

@ -18,6 +18,8 @@ class RestResponseComponent extends Component
)
);
public $signContents = false;
private $__setup = false;
/** @var array */
@ -46,7 +48,7 @@ class RestResponseComponent extends Component
'restSearch' => array(
'description' => "Search MISP using a list of filter parameters and return the data in the selected format. The search is available on an event and an attribute level, just select the scope via the URL (/events/restSearch vs /attributes/restSearch). Besides the parameters listed, other, format specific ones can be passed along (for example: requested_attributes and includeContext for the CSV export). This API allows pagination via the page and limit parameters.",
'mandatory' => array('returnFormat'),
'optional' => array('page', 'limit', 'value' , 'type', 'category', 'org', 'tags', 'date', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'attribute_timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'includeEventTags', 'event_timestamp', 'threat_level_id', 'eventinfo', 'includeProposals', 'includeDecayScore', 'includeFullModel', 'decayingModel', 'excludeDecayed', 'score', 'first_seen', 'last_seen'),
'optional' => array('page', 'limit', 'value' , 'type', 'category', 'org', 'tags', 'date', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'attribute_timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'includeEventTags', 'event_timestamp', 'threat_level_id', 'eventinfo', 'sharinggroup', 'includeProposals', 'includeDecayScore', 'includeFullModel', 'decayingModel', 'excludeDecayed', 'score', 'first_seen', 'last_seen'),
'params' => array()
),
'addTag' => array(
@ -86,7 +88,7 @@ class RestResponseComponent extends Component
'restSearch' => array(
'description' => "Search MISP using a list of filter parameters and return the data in the selected format. The search is available on an event and an attribute level, just select the scope via the URL (/events/restSearch vs /attributes/restSearch). Besides the parameters listed, other, format specific ones can be passed along (for example: requested_attributes and includeContext for the CSV export). This API allows pagination via the page and limit parameters.",
'mandatory' => array('returnFormat'),
'optional' => array('page', 'limit', 'value', 'type', 'category', 'org', 'tag', 'tags', 'searchall', 'date', 'last', 'eventid', 'withAttachments', 'metadata', 'uuid', 'published', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'sgReferenceOnly', 'eventinfo', 'excludeLocalTags', 'threat_level_id'),
'optional' => array('page', 'limit', 'value', 'type', 'category', 'org', 'tag', 'tags', 'event_tags', 'searchall', 'date', 'last', 'eventid', 'withAttachments', 'metadata', 'uuid', 'published', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'sgReferenceOnly', 'eventinfo', 'sharinggroup', 'excludeLocalTags', 'threat_level_id'),
'params' => array()
),
'addTag' => array(
@ -365,12 +367,7 @@ class RestResponseComponent extends Component
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
$url = $this->generateUrl($controller, $action);
$result[$url] = $data;
}
}
@ -378,6 +375,25 @@ class RestResponseComponent extends Component
return $result;
}
/**
* @param array $user
* @return array
*/
public function getAccessibleApis(array $user)
{
$output = [];
foreach ($this->__descriptions as $controller => $actions) {
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$url = $this->generateUrl($controller, $action);
$output[$controller][$action] = $url;
}
}
}
return $output;
}
public function getAllApis($user)
{
$this->__setup();
@ -387,15 +403,10 @@ class RestResponseComponent extends Component
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
$data['api_name'] = '[' . $controller . '] ' . $action;
$data['controller'] = $controller;
$data['action'] = $action;
$data['body'] = array();
$body = [];
$filter_types = array('mandatory', 'optional');
foreach ($filter_types as $filter_type) {
if (!empty($data[$filter_type])) {
@ -405,16 +416,16 @@ class RestResponseComponent extends Component
}
foreach ($filter_items as $filter) {
if ($filter === lcfirst($filter)) {
$data['body'][$filter] = $filter_type;
$body[$filter] = $filter_type;
} else {
$data['body'][$filter] = array($filter_type);
$body[$filter] = array($filter_type);
}
}
}
}
}
$data['body'] = json_encode($data['body'], JSON_PRETTY_PRINT);
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
$data['body'] = $body;
$url = $this->generateUrl($controller, $action);;
$data['url'] = $url;
if (!empty($data['params'])) {
foreach ($data['params'] as $param) {
@ -468,39 +479,70 @@ class RestResponseComponent extends Component
return [];
}
public function saveFailResponse($controller, $action, $id = false, $validationErrors, $format = false, $data = null)
/**
* @param string $controller
* @param string $action
* @param int|false $id
* @param mixed $validationErrors
* @param string|false $format
* @param mixed $data
* @return CakeResponse
* @throws Exception
*/
public function saveFailResponse($controller, $action, $id, $validationErrors, $format = false, $data = null)
{
$response = array();
$action = $this->__dissectAdminRouting($action);
$stringifiedAction = $action['action'];
if (isset(self::CONVERT_ACTION_TO_MESSAGE[$controller][$action['action']])) {
$stringifiedAction = self::CONVERT_ACTION_TO_MESSAGE[$controller][$action['action']];
if (isset(self::CONVERT_ACTION_TO_MESSAGE[$controller][$stringifiedAction])) {
$stringifiedAction = self::CONVERT_ACTION_TO_MESSAGE[$controller][$stringifiedAction];
}
$response['saved'] = false;
$response['name'] = 'Could not ' . $stringifiedAction . ' ' . Inflector::singularize($controller);
$response['message'] = $response['name'];
$message = 'Could not ' . $stringifiedAction . ' ' . Inflector::singularize($controller);
$response = [
'saved' => false,
'name' => $message,
'message' => $message,
'url' => $this->__generateURL($action, $controller, $id),
'errors' => $validationErrors,
];
if ($data !== null) {
$response['data'] = $data;
}
$response['url'] = $this->__generateURL($action, $controller, $id);
$response['errors'] = $validationErrors;
if ($id) {
$response['id'] = $id;
}
return $this->__sendResponse($response, 403, $format);
}
/**
* @param string $controller
* @param string $action
* @param int|false $id
* @param string|false $format
* @param string|false $message
* @param mixed $data
* @return CakeResponse
* @throws Exception
*/
public function saveSuccessResponse($controller, $action, $id = false, $format = false, $message = false, $data = null)
{
$action = $this->__dissectAdminRouting($action);
if (!$message) {
$message = Inflector::singularize($controller) . ' ' . $action['action'] . ((substr($action['action'], -1) == 'e') ? 'd' : 'ed');
$message = Inflector::singularize($controller) . ' ' . $action['action'] . ((substr($action['action'], -1) === 'e') ? 'd' : 'ed');
}
$response['saved'] = true;
$response['success'] = true;
$response['name'] = $message;
$response['message'] = $response['name'];
$response = [
'saved' => true,
'success' => true,
'name' => $message,
'message' => $message,
'url' => $this->__generateURL($action, $controller, $id),
];
if ($data !== null) {
$response['data'] = $data;
}
$response['url'] = $this->__generateURL($action, $controller, $id);
if ($id) {
$response['id'] = $id;
}
return $this->__sendResponse($response, 200, $format);
}
@ -516,7 +558,7 @@ class RestResponseComponent extends Component
*/
private function __sendResponse($response, $code, $format = false, $raw = false, $download = false, $headers = array())
{
$format = strtolower($format);
$format = !empty($format) ? strtolower($format) : 'json';
if ($format === 'application/xml' || $format === 'xml') {
if (!$raw) {
if (isset($response[0])) {
@ -539,11 +581,7 @@ class RestResponseComponent extends Component
} elseif ($format === 'csv' || $format === 'text/csv') {
$type = 'csv';
} else {
if (empty($format)) {
$type = 'json';
} else {
$type = $format;
}
$type = $format;
$dumpSql = !empty($this->Controller->sql_dump) && Configure::read('debug') > 1;
if (!$raw) {
if (is_string($response)) {
@ -590,11 +628,36 @@ class RestResponseComponent extends Component
}
if ($response instanceof TmpFileTool) {
App::uses('CakeResponseFile', 'Tools');
$cakeResponse = new CakeResponseFile(['status' => $code, 'type' => $type]);
$cakeResponse->file($response);
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$etag = '"' . $response->hash('sha1') . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
return new CakeResponse(['status' => 304]);
}
$headers['ETag'] = $etag;
}
if ($this->signContents) {
$data = $response->intoString();
$headers['x-pgp-signature'] = $this->sign($data);
$cakeResponse = new CakeResponse(['body' => $data, 'status' => $code, 'type' => $type]);
} else {
App::uses('CakeResponseFile', 'Tools');
$cakeResponse = new CakeResponseFile(['status' => $code, 'type' => $type]);
$cakeResponse->file($response);
}
} else {
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
// Check if resource was changed when `If-None-Match` header is send and return 304 Not Modified
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$etag = '"' . sha1($response) . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
return new CakeResponse(['status' => 304]);
}
// Generate etag just when HTTP_IF_NONE_MATCH is set
$headers['ETag'] = $etag;
}
$cakeResponse = new CakeResponse(['body' => $response, 'status' => $code, 'type' => $type]);
if ($this->signContents) {
$headers['x-pgp-signature'] = $this->sign($response);
}
}
if (Configure::read('Security.allow_cors')) {
@ -618,6 +681,25 @@ class RestResponseComponent extends Component
return $cakeResponse;
}
/**
* @param string $response
* @return string Signature as base64 encoded string
* @throws Crypt_GPG_BadPassphraseException
* @throws Crypt_GPG_Exception
* @throws Crypt_GPG_KeyNotFoundException
* @throws Exception
*/
private function sign($response)
{
/** @var CryptographicKey $cryptographicKey */
$cryptographicKey = ClassRegistry::init('CryptographicKey');
$signature = $cryptographicKey->signWithInstanceKey($response);
if (!$signature) {
throw new Exception('Could not sign data.');
}
return base64_encode($signature);
}
/**
* Detect if request comes from automatic tool (like other MISP instance or PyMISP) or AJAX
* @return bool
@ -719,13 +801,8 @@ class RestResponseComponent extends Component
$scopes = array('Event', 'Attribute', 'Sighting');
foreach ($scopes as $scope) {
$this->{$scope} = ClassRegistry::init($scope);
$this->__descriptions[$scope]['restSearch'] = array(
'description' => $this->__descriptions[$scope]['restSearch']['description'],
'returnFormat' => array_keys($this->{$scope}->validFormats),
'mandatory' => $this->__descriptions[$scope]['restSearch']['mandatory'],
'optional' => $this->__descriptions[$scope]['restSearch']['optional'],
'params' => $this->__descriptions[$scope]['restSearch']['params']
);
$returnFormat = array_keys($this->{$scope}->validFormats);
$this->__descriptions[$scope]['restSearch']['returnFormat'] = $returnFormat;
}
$this->__configureFieldConstraints();
$this->__setupFieldsConstraint();
@ -1926,7 +2003,8 @@ class RestResponseComponent extends Component
if ($values === null) {
$tagModel = ClassRegistry::init("Tag");
$tags = $tagModel->find('column', array(
'fields' => array('Tag.name')
'fields' => array('Tag.name'),
'callbacks' => false,
));
$values = [];
foreach ($tags as $tag) {
@ -1951,19 +2029,40 @@ class RestResponseComponent extends Component
private function __overwriteAction($scope, $action, &$field) {
$field['values'] = array_keys(ClassRegistry::init("Log")->actionDefinitions);
}
private function __overwriteRoleId($scope, $action, &$field) {
$this->{$scope} = ClassRegistry::init("Role");
$roles = $this->{$scope}->find('list', array(
'fields' => array('id', 'name')
));
$field['values'] = [];
foreach ($roles as $id => $name) {
$field['values'][] = ['label' => $name, 'value' => $id];
private function __overwriteRoleId($scope, $action, &$field)
{
static $values;
if ($values === null) {
$roleModel = ClassRegistry::init("Role");
$roles = $roleModel->find('list', array(
'fields' => array('id', 'name')
));
$values = [];
foreach ($roles as $id => $name) {
$values[] = ['label' => $name, 'value' => $id];
}
}
$field['values'] = $values;
}
private function __overwriteSeen($scope, $action, &$field) {
if ($action == 'restSearch') {
$field['help'] = __('Seen within the last x amount of time, where x can be defined in days, hours, minutes (for example 5d or 12h or 30m)');
}
}
/**
* @param string $controller
* @param string $action
* @return string
*/
private function generateUrl($controller, $action)
{
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
return '/' . $admin_routing . $controller . '/' . $action;
}
}

View File

@ -122,8 +122,8 @@ class CorrelationExclusionsController extends AppController
} else {
$this->set('title', __('Clean up correlations'));
$this->set('question', __('Execute the cleaning of all correlations that are at odds with the exclusion rules? This will delete all matching correlations.'));
$this->set('actionName', 'clean');
$this->layout = 'ajax';
$this->set('actionName', __('Clean'));
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}

View File

@ -44,6 +44,9 @@ class CorrelationsController extends AppController
}
}
}
$this->__setPagingParams($query['page'], $query['limit'], count($data), 'named');
$this->set('age', $age);
$this->set('age_unit', $unit);
$this->set('data', $data);
@ -72,4 +75,150 @@ class CorrelationsController extends AppController
$this->redirect(['controller' => 'correlations', 'action' => 'top']);
}
}
public function overCorrelations()
{
$query = [
'limit' => 50,
'page' => 1,
'order' => 'occurrence desc'
];
foreach ($query as $customParam => $foo) {
if (isset($this->request->params['named'][$customParam])) {
$query[$customParam] = $this->request->params['named'][$customParam];
}
}
if (isset($this->request->params['named']['scope'])) {
$limit = $this->Correlation->OverCorrelatingValue->getLimit();
if ($this->request->params['named']['scope'] === 'over_correlating') {
$scope = 'over_correlating';
$query['conditions'][] = ['occurrence >=' => $limit];
} else if ($this->request->params['named']['scope'] === 'not_over_correlating') {
$query['conditions'][] = ['occurrence <' => $limit];
$scope = 'not_over_correlating';
}
} else {
$scope = 'all';
}
$data = $this->Correlation->OverCorrelatingValue->getOverCorrelations($query);
$data = $this->Correlation->attachExclusionsToOverCorrelations($data);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, 'json');
}
$this->__setPagingParams($query['page'], $query['limit'], count($data), 'named');
$this->set('data', $data);
$this->set('scope', $scope);
$this->set('title_for_layout', __('Index of over correlating values'));
$this->set('menuData', [
'menuList' => 'correlationExclusions',
'menuItem' => 'over'
]);
}
public function switchEngine(string $engine)
{
$this->loadModel('Server');
if (!isset($this->Correlation->validEngines[$engine])) {
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
}
if ($this->request->is('post')) {
$setting = $this->Server->getSettingData('MISP.correlation_engine');
$result = $this->Server->serverSettingsEditValue($this->Auth->user(), $setting, $engine);
if ($result === true) {
$message = __('Engine switched.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Correlations', 'switchEngine', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
} else {
$message = __('Couldn\'t switch to the requested engine.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Correlations', 'switchEngine', false, $message, $this->response->type());
} else {
$this->Flash->error($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
}
} else {
$this->set('engine', $engine);
$this->render('ajax/switch_engine_confirmation');
}
}
public function truncate(string $engine)
{
if (!isset($this->Correlation->validEngines[$engine])) {
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
}
if ($this->request->is('post')) {
if (!Configure::read('MISP.background_jobs')) {
$result = $this->Correlation->truncate($this->Auth->user(), $engine);
$message = $result ? __('Table truncated.') : __('Could not truncate table');
if ($this->_isRest()) {
if ($result) {
$this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
} else {
$this->RestResponse->saveFailResponse('Correlations', 'truncate', false, $message, $this->response->type());
}
} else {
$this->Flash->{$result ? 'success' : 'error'}($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
} else {
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'truncate table',
$this->Correlation->validEngines[$engine],
'Job created.'
);
$this->Correlation->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'truncateTable',
$this->Auth->user('id'),
$engine,
$jobId
],
true,
$jobId
);
$message = __('Job queued. You can view the progress if you navigate to the active jobs view (Administration -> Jobs).');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
}
} else {
$this->set('engine', $engine);
$this->set('table_name', $this->Correlation->validEngines[$engine]);
$this->render('ajax/truncate_confirmation');
}
}
public function generateOccurrences()
{
$this->loadModel('OverCorrelatingValue');
$this->OverCorrelatingValue->generateOccurrencesRouter();
if (Configure::read('MISP.background_jobs')) {
$message = __('Job queued.');
} else {
$message = __('Over-correlations counted successfully.');
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Correlations', 'generateOccurrences', false, $this->response->type(), $message);
}
$this->Flash->info($message);
$this->redirect(['controller' => 'correlations', 'action' => 'overCorrelations']);
}
}

View File

@ -0,0 +1,101 @@
<?php
App::uses('AppController', 'Controller');
/**
* @property CryptographicKey $CryptographicKey
*/
class CryptographicKeysController extends AppController
{
public $components = array('Session', 'RequestHandler');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999
);
public function add($type, $parent_id)
{
if (empty($type) || empty($parent_id)) {
throw new MethodNotAllowedException(__('No type and/or parent_id supplied.'));
}
if ($type === 'Event') {
$existingEvent = $this->CryptographicKey->Event->fetchSimpleEvent(
$this->Auth->user(),
$parent_id,
[
'conditions' => [
'Event.orgc_id' => $this->Auth->user('org_id')
]
]
);
if (empty($existingEvent)) {
throw new MethodNotAllowedException(__('Invalid Event.'));
}
}
$params = [
'beforeSave' => function ($data) use($type, $parent_id) {
$data['CryptographicKey']['parent_type'] = $type;
$data['CryptographicKey']['parent_id'] = $parent_id;
return $data;
},
'redirect' => [
'controller' => Inflector::tableize($type),
'action' => 'view',
$parent_id
]
];
$this->CRUD->add($params);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
$instanceKey = file_exists(APP . 'webroot/gpg.asc') ? FileAccessTool::readFromFile(APP . 'webroot/gpg.asc') : '';
$this->set('instanceKey', $instanceKey);
$this->set('menuData', array('menuList' => 'cryptographic_keys', 'menuItem' => 'add_cryptographic_key'));
}
public function delete($id)
{
$user = $this->Auth->user();
$this->CRUD->delete($id, [
'beforeDelete' => function ($data) use($user) {
$parent_type = $data['CryptographicKey']['parent_type'];
$tempModel = ClassRegistry::init($parent_type);
$existingData = $tempModel->find('first', [
'conditions' => [
$parent_type . '.id' => $data['CryptographicKey']['parent_id']
],
'recursive' => -1
]);
if ($parent_type === 'Event') {
if (!$user['Role']['perm_site_admin'] && $existingData['Event']['orgc_id'] !== $user['org_id']) {
return false;
}
}
return $data;
}
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function view($id)
{
$key = $this->CryptographicKey->find('first', [
'recursive' => -1,
'fields' => ['id', 'type', 'key_data', 'fingerprint'],
'conditions' => ['CryptographicKey.id' => $id]
]);
$this->set('id', $id);
$this->set('title', __('Viewing %s key #%s', h($key['CryptographicKey']['type']), h($key['CryptographicKey']['id'])));
$this->set(
'html',
sprintf(
'<span class="quickSelect">%s</span>',
nl2br(h($key['CryptographicKey']['key_data']))
)
);
$this->layout = false;
$this->render('/genericTemplates/display');
}
}

View File

@ -12,17 +12,20 @@ class DashboardsController extends AppController
public function beforeFilter()
{
parent::beforeFilter();
$this->Security->unlockedActions = array_merge(array('renderWidget', 'getForm'), $this->Security->unlockedActions);
$this->Security->unlockedActions[] = 'renderWidget';
$this->Security->unlockedActions[] = 'getForm';
if ($this->request->action === 'renderWidget') {
$this->Security->doNotGenerateToken = true;
}
}
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999
'limit' => 60,
'maxLimit' => 9999
);
public function index($template_id = false)
{
$this->loadModel('UserSetting');
if (empty($template_id)) {
$params = array(
'conditions' => array(
@ -30,7 +33,7 @@ class DashboardsController extends AppController
'UserSetting.setting' => 'dashboard'
)
);
$userSettings = $this->UserSetting->find('first', $params);
$userSettings = $this->User->UserSetting->find('first', $params);
} else {
$dashboardTemplate = $this->Dashboard->getDashboardTemplate($this->Auth->user(), $template_id);
if (empty($dashboardTemplate)) {
@ -80,7 +83,6 @@ class DashboardsController extends AppController
// continue, we just don't load the widget
}
}
$this->layout = 'dashboard';
$this->set('widgets', $widgets);
}
@ -112,22 +114,20 @@ class DashboardsController extends AppController
public function updateSettings()
{
if ($this->request->is('post')) {
$this->UserSetting = ClassRegistry::init('UserSetting');
if (!isset($this->request->data['Dashboard']['value'])) {
throw new InvalidArgumentException(__('No setting data found.'));
}
$data = array(
'UserSetting' => array(
'user_id' => $this->Auth->user('id'),
'setting' => 'dashboard',
'value' => $this->request->data['Dashboard']['value']
)
);
$result = $this->UserSetting->setSetting($this->Auth->user(), $data);
$result = $this->User->UserSetting->setSetting($this->Auth->user(), $data);
if ($result) {
return $this->RestResponse->saveSuccessResponse('Dashboard', 'updateSettings', false, false, __('Settings updated.'));
}
return $this->RestResponse->saveFailResponse('Dashboard', 'updateSettings', false, $this->UserSetting->validationErrors, $this->response->type());
return $this->RestResponse->saveFailResponse('Dashboard', 'updateSettings', false, $this->User->UserSetting->validationErrors, $this->response->type());
}
}
@ -155,7 +155,7 @@ class DashboardsController extends AppController
}
$user = $this->Auth->user();
@session_write_close(); // allow concurrent AJAX requests (session hold lock by default)
@session_abort(); // allow concurrent AJAX requests (session hold lock by default)
if (empty($this->request->data['data'])) {
$this->request->data = array('data' => $this->request->data);
@ -237,7 +237,6 @@ class DashboardsController extends AppController
public function saveTemplate($update = false)
{
$this->loadModel('UserSetting');
if (!empty($update)) {
$conditions = array('Dashboard.id' => $update);
if (Validation::uuid($update)) {
@ -260,7 +259,7 @@ class DashboardsController extends AppController
}
$data = $this->request->data;
if (empty($update)) { // save the template stored in user setting and make it persistent
$data['value'] = $this->UserSetting->getSetting($this->Auth->user('id'), 'dashboard');
$data['value'] = $this->User->UserSetting->getSetting($this->Auth->user('id'), 'dashboard');
}
$result = $this->Dashboard->saveDashboardTemplate($this->Auth->user(), $data, $update);
if ($this->_isRest()) {
@ -279,7 +278,6 @@ class DashboardsController extends AppController
} else {
$this->layout = false;
}
$this->loadModel('User');
$permFlags = array(0 => __('Unrestricted'));
foreach ($this->User->Role->permFlags as $perm_flag => $perm_data) {
$permFlags[$perm_flag] = $perm_data['text'];

View File

@ -9,7 +9,7 @@ class DecayingModelController extends AppController
public $paginate = array(
'limit' => 50,
'order' => array(
'DecayingModel.ID' => 'desc'
'DecayingModel.ID' => 'desc'
)
);
@ -43,28 +43,12 @@ class DecayingModelController extends AppController
{
if ($this->request->is('post') || $this->request->is('put')) {
$data = $this->request->data['DecayingModel'];
if ($data['submittedjson']['name'] != '' && $data['json'] != '') {
throw new MethodNotAllowedException(__('Only one import field can be used'));
}
if ($data['submittedjson']['size'] > 0) {
$filename = basename($data['submittedjson']['name']);
$file_content = file_get_contents($data['submittedjson']['tmp_name']);
if ((isset($data['submittedjson']['error']) && $data['submittedjson']['error'] == 0) ||
(!empty($data['submittedjson']['tmp_name']) && $data['submittedjson']['tmp_name'] != '')
) {
if (!$file_content) {
throw new InternalErrorException(__('PHP says file was not uploaded. Are you attacking me?'));
}
}
$text = $file_content;
} else {
$text = $data['json'];
}
$text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
$json = json_decode($text, true);
if ($json === null) {
throw new MethodNotAllowedException(__('Error while decoding JSON'));
}
unset($json['id']);
unset($json['uuid']);
$json['default'] = 0;

View File

@ -14,8 +14,8 @@ class EventReportsController extends AppController
public $paginate = array(
'limit' => 60,
'order' => array(
'EventReport.event_id' => 'ASC',
'EventReport.name' => 'ASC'
'EventReport.event_id' => 'ASC',
'EventReport.name' => 'ASC'
),
'recursive' => -1,
'contain' => array(
@ -78,8 +78,9 @@ class EventReportsController extends AppController
if (!$this->_isRest()) {
throw new MethodNotAllowedException(__('This function can only be reached via the API.'));
}
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
$proxyMISPElements = $this->EventReport->getProxyMISPElements($this->Auth->user(), $report['EventReport']['event_id']);
$user = $this->_closeSession();
$report = $this->EventReport->simpleFetchById($user, $reportId);
$proxyMISPElements = $this->EventReport->getProxyMISPElements($user, $report['EventReport']['event_id']);
return $this->RestResponse->viewData($proxyMISPElements, $this->response->type());
}
@ -139,7 +140,7 @@ class EventReportsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
} else {
$this->layout = 'ajax';
$this->layout = false;
$this->set('report', $report);
$this->render('ajax/delete');
}
@ -163,7 +164,7 @@ class EventReportsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
} else {
$this->layout = 'ajax';
$this->layout = false;
$this->set('report', $report);
}
}
@ -178,7 +179,7 @@ class EventReportsController extends AppController
$reports = $this->EventReport->find('all', [
'recursive' => -1,
'conditions' => $compiledConditions,
'contain' => $this->EventReport->defaultContain,
'contain' => EventReport::DEFAULT_CONTAIN,
]);
return $this->RestResponse->viewData($reports, $this->response->type());
} else {
@ -201,6 +202,8 @@ class EventReportsController extends AppController
$fetcherModule = $this->EventReport->isFetchURLModuleEnabled();
$this->set('importModuleEnabled', is_array($fetcherModule));
$this->render('ajax/indexForEvent');
} else {
$this->set('title_for_layout', __('Event Reports'));
}
}
}
@ -230,7 +233,7 @@ class EventReportsController extends AppController
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'applySuggestions', $reportId);
}
}
$this->layout = 'ajax';
$this->layout = false;
$this->set('reportId', $reportId);
$this->render('ajax/extractAllFromReport');
}
@ -286,7 +289,7 @@ class EventReportsController extends AppController
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'applySuggestions', $reportId);
}
}
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/replaceSuggestionInReport');
}
}
@ -325,7 +328,7 @@ class EventReportsController extends AppController
}
$this->set('importModuleEnabled', is_array($fetcherModule));
$this->set('event_id', $event_id);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/importReportFromUrl');
}
@ -367,7 +370,7 @@ class EventReportsController extends AppController
}
}
$this->set('event_id', $eventId);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/reportFromEvent');
}

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,7 @@ class FavouriteTagsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException('This action is available via AJAX only.');
}
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/getToggleField');
}
}

View File

@ -298,6 +298,9 @@ class FeedsController extends AppController
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->loadModel('Server');
$allTypes = $this->Server->getAllTypes();
$dropdownData = [
'orgs' => $this->Event->Orgc->find('list', array(
'fields' => array('id', 'name'),
@ -309,9 +312,12 @@ class FeedsController extends AppController
'distributionLevels' => $distributionLevels,
'inputSources' => $inputSources
];
$this->set('allAttributeTypes', $allTypes['attribute']);
$this->set('allObjectTypes', $allTypes['object']);
$this->set(compact('dropdownData'));
$this->set('defaultPullRules', json_encode(Feed::DEFAULT_FEED_PULL_RULES));
$this->set('menuData', array('menuList' => 'feeds', 'menuItem' => 'add'));
$this->set('pull_scope', 'feed');
}
private function __checkRegex($pattern)
@ -346,10 +352,14 @@ class FeedsController extends AppController
'delete_local_file',
'lookup_visible',
'headers',
'orgc_id'
'orgc_id',
'fixed_event'
],
'afterFind' => function (array $feed) {
$feed['Feed']['settings'] = json_decode($feed['Feed']['settings'], true);
if ($feed['Feed']['source_format'] == 'misp' && empty($feed['Feed']['rules'])) {
$feed['Feed']['rules'] = json_encode(Feed::DEFAULT_FEED_PULL_RULES);
}
return $feed;
},
@ -437,6 +447,12 @@ class FeedsController extends AppController
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->loadModel('Server');
$allTypes = $this->Server->getAllTypes();
$this->set('allAttributeTypes', $allTypes['attribute']);
$this->set('allObjectTypes', $allTypes['object']);
$this->set('supportedUrlparams', Feed::SUPPORTED_URL_PARAM_FILTERS);
$dropdownData = [
'orgs' => $this->Event->Orgc->find('list', array(
'fields' => array('id', 'name'),
@ -458,6 +474,7 @@ class FeedsController extends AppController
if(!empty($this->request->data['Feed']['rules'])){
$this->request->data['Feed']['pull_rules'] = $this->request->data['Feed']['rules'];
}
$this->set('pull_scope', 'feed');
$this->render('add');
}

View File

@ -1,6 +1,9 @@
<?php
App::uses('AppController', 'Controller');
/**
* @property Galaxy $Galaxy
*/
class GalaxiesController extends AppController
{
public $components = array('Session', 'RequestHandler');
@ -216,23 +219,7 @@ class GalaxiesController extends AppController
$clusters = $this->request->data;
} else {
$data = $this->request->data['Galaxy'];
if ($data['submittedjson']['name'] != '' && $data['json'] != '') {
throw new MethodNotAllowedException(__('Only one import field can be used at a time'));
}
if ($data['submittedjson']['size'] > 0) {
$filename = basename($data['submittedjson']['name']);
$file_content = file_get_contents($data['submittedjson']['tmp_name']);
if ((isset($data['submittedjson']['error']) && $data['submittedjson']['error'] == 0) ||
(!empty($data['submittedjson']['tmp_name']) && $data['submittedjson']['tmp_name'] != '')
) {
if (!$file_content) {
throw new InternalErrorException(__('PHP says file was not uploaded. Are you attacking me?'));
}
}
$text = $file_content;
} else {
$text = $data['json'];
}
$text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
$clusters = json_decode($text, true);
if ($clusters === null) {
throw new MethodNotAllowedException(__('Error while decoding JSON'));
@ -332,17 +319,17 @@ class GalaxiesController extends AppController
public function selectGalaxy($target_id, $target_type='event', $namespace='misp', $noGalaxyMatrix = false)
{
$this->_closeSession();
$mitreAttackGalaxyId = $this->Galaxy->getMitreAttackGalaxyId();
$local = !empty($this->params['named']['local']) ? $this->params['named']['local'] : '0';
$eventid = !empty($this->params['named']['eventid']) ? $this->params['named']['eventid'] : '0';
$conditions = $namespace === '0' ? array() : array('namespace' => $namespace);
$conditions[] = [
'enabled' => true
];
if(!$local) {
$conditions[] = [
'local_only' => 0
];
$conditions = ['enabled' => true];
if ($namespace !== '0') {
$conditions['namespace'] = $namespace;
}
if (!$local) {
$conditions['local_only'] = false;
}
$galaxies = $this->Galaxy->find('all', array(
'recursive' => -1,
@ -396,21 +383,21 @@ class GalaxiesController extends AppController
public function selectGalaxyNamespace($target_id, $target_type='event', $noGalaxyMatrix = false)
{
$namespaces = $this->Galaxy->find('list', array(
$this->_closeSession();
$namespaces = $this->Galaxy->find('column', array(
'recursive' => -1,
'fields' => array('namespace', 'namespace'),
'fields' => array('namespace'),
'conditions' => array('enabled' => 1),
'group' => array('namespace'),
'unique' => true,
'order' => array('namespace asc')
));
$local = !empty($this->params['named']['local']) ? '1' : '0';
$eventid = !empty($this->params['named']['eventid']) ? $this->params['named']['eventid'] : '0';
$items = array();
$noGalaxyMatrix = $noGalaxyMatrix ? '1' : '0';
$items[] = array(
$items = [[
'name' => __('All namespaces'),
'value' => $this->baseurl . "/galaxies/selectGalaxy/" . $target_id . '/' . $target_type . '/0' . '/' . $noGalaxyMatrix . '/local:' . $local . '/eventid:' . $eventid
);
]];
foreach ($namespaces as $namespace) {
$items[] = array(
'name' => $namespace,
@ -427,7 +414,7 @@ class GalaxiesController extends AppController
public function selectCluster($target_id, $target_type = 'event', $selectGalaxy = false)
{
$conditions = array();
$user = $this->_closeSession();
$conditions = array(
'OR' => array(
'GalaxyCluster.published' => true,
@ -443,92 +430,79 @@ class GalaxiesController extends AppController
if ($selectGalaxy) {
$conditions['GalaxyCluster.galaxy_id'] = $selectGalaxy;
}
$local = !empty($this->params['named']['local']) ? $this->params['named']['local'] : '0';
$data = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array(
'conditions' => $conditions,
'fields' => array('value', 'description', 'source', 'type', 'id', 'uuid'),
'order' => array('value asc'),
), false);
$clusters = array();
$cluster_ids = array();
foreach ($data as $k => $cluster) {
$cluster_ids[] = $cluster['GalaxyCluster']['id'];
}
$data = array_column($this->Galaxy->GalaxyCluster->fetchGalaxyClusters($user, array(
'conditions' => $conditions,
'fields' => array('value', 'description', 'source', 'type', 'id', 'uuid'),
'order' => array('value asc'),
)), 'GalaxyCluster');
$synonyms = $this->Galaxy->GalaxyCluster->GalaxyElement->find('all', array(
'conditions' => array(
'GalaxyElement.galaxy_cluster_id' => $cluster_ids,
'GalaxyElement.key' => 'synonyms'
'GalaxyElement.key' => 'synonyms',
$conditions
),
'fields' => ['GalaxyElement.galaxy_cluster_id', 'GalaxyElement.value'],
'contain' => 'GalaxyCluster',
'recursive' => -1
));
$sorted_synonyms = array();
$sortedSynonyms = array();
foreach ($synonyms as $synonym) {
$sorted_synonyms[$synonym['GalaxyElement']['galaxy_cluster_id']][] = $synonym;
$sortedSynonyms[$synonym['GalaxyElement']['galaxy_cluster_id']][] = $synonym['GalaxyElement']['value'];
}
foreach ($data as $k => $cluster) {
$cluster['GalaxyCluster']['synonyms_string'] = array();
if (!empty($sorted_synonyms[$cluster['GalaxyCluster']['id']])) {
foreach ($sorted_synonyms[$cluster['GalaxyCluster']['id']] as $element) {
$cluster['GalaxyCluster']['synonyms_string'][] = $element['GalaxyElement']['value'];
$cluster['GalaxyElement'][] = $element['GalaxyElement'];
}
unset($sorted_synonyms[$cluster['GalaxyCluster']['id']]);
$clusters = [];
foreach ($data as $cluster) {
if (!empty($sortedSynonyms[$cluster['id']])) {
$cluster['synonyms_string'] = implode(', ', $sortedSynonyms[$cluster['id']]);
}
$cluster['GalaxyCluster']['synonyms_string'] = implode(', ', $cluster['GalaxyCluster']['synonyms_string']);
unset($cluster['GalaxyElement']);
$clusters[$cluster['GalaxyCluster']['type']][$cluster['GalaxyCluster']['uuid']] = $cluster['GalaxyCluster'];
$clusters[$cluster['type']][$cluster['uuid']] = $cluster;
}
ksort($clusters);
$this->set('target_id', $target_id);
$this->set('target_type', $target_type);
$items = array();
foreach ($clusters as $namespace => $cluster_data) {
foreach ($cluster_data as $k => $cluster) {
$name = $cluster['value'];
foreach ($clusters as $cluster_data) {
foreach ($cluster_data as $cluster) {
$optionName = $cluster['value'];
if ($cluster['synonyms_string'] !== '') {
$synom = __('Synonyms: ') . $cluster['synonyms_string'];
$optionName .= $cluster['synonyms_string'] !== '' ? ' (' . $cluster['synonyms_string'] . ')' : '';
} else {
$synom = '';
if (isset($cluster['synonyms_string'])) {
$optionName .= ' (' . $cluster['synonyms_string'] . ')';
}
$itemParam = array(
'name' => $optionName,
'value' => $cluster['id'],
'template' => array(
'name' => $name,
'name' => $cluster['value'],
'infoExtra' => $cluster['description'],
),
'additionalData' => array(
'uuid' => $cluster['uuid']
)
);
if ($cluster['synonyms_string'] !== '') {
$itemParam['template']['infoContextual'] = $synom;
if (isset($cluster['synonyms_string'])) {
$itemParam['template']['infoContextual'] = __('Synonyms: ') . $cluster['synonyms_string'];
}
$items[] = $itemParam;
unset($cluster_data[$k]);
}
}
$onClickForm = 'quickSubmitGalaxyForm';
if ($this->_isRest()) {
return $this->RestResponse->viewData($items, $this->response->type());
} else {
$this->set('items', $items);
$this->set('options', array( // set chosen (select picker) options
'functionName' => $onClickForm,
'multiple' => $target_type == 'galaxyClusterRelation' ? 0 : '-1',
'select_options' => array(
'additionalData' => array(
'target_id' => $target_id,
'target_type' => $target_type,
'local' => $local
)
),
));
$this->render('ajax/cluster_choice');
}
$mirrorOnEventEnabled = Configure::read("MISP.enable_clusters_mirroring_from_attributes_to_event");
$mirrorOnEvent = $mirrorOnEventEnabled && $target_type == 'attribute';
$this->set('target_id', $target_id);
$this->set('target_type', $target_type);
$this->set('mirrorOnEvent', $mirrorOnEvent);
$this->set('items', $items);
$local = !empty($this->params['named']['local']) ? $this->params['named']['local'] : '0';
$this->set('options', array( // set chosen (select picker) options
'functionName' => 'quickSubmitGalaxyForm',
'multiple' => $target_type == 'galaxyClusterRelation' ? 0 : '-1',
'select_options' => array(
'additionalData' => array(
'target_id' => $target_id,
'target_type' => $target_type,
'local' => $local
)
),
));
$this->render('ajax/cluster_choice');
}
public function attachCluster($target_id, $target_type = 'event')
@ -542,7 +516,10 @@ class GalaxiesController extends AppController
public function attachMultipleClusters($target_id, $target_type = 'event')
{
$local = !empty($this->params['named']['local']);
$mirrorOnEventEnabled = Configure::read("MISP.enable_clusters_mirroring_from_attributes_to_event");
$mirrorOnEvent = $mirrorOnEventEnabled && $target_type == 'attribute';
$this->set('local', $local);
$this->set('mirrorOnEvent', $mirrorOnEvent);
if ($this->request->is('post')) {
if ($target_id === 'selected') {
$target_id_list = json_decode($this->request->data['Galaxy']['attribute_ids']);
@ -550,14 +527,25 @@ class GalaxiesController extends AppController
$target_id_list = array($target_id);
}
$cluster_ids = $this->request->data['Galaxy']['target_ids'];
$mirrorOnEventRequested = $mirrorOnEvent && !empty($this->request->data['Galaxy']['mirror_on_event']);
if (strlen($cluster_ids) > 0) {
$cluster_ids = json_decode($cluster_ids, true);
if ($cluster_ids === null || empty($cluster_ids)) {
$cluster_ids = $this->_jsonDecode($cluster_ids);
if (empty($cluster_ids)) {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Failed to parse request or no clusters picked.'))), 'status'=>200, 'type' => 'json'));
}
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Failed to parse request.'))), 'status'=>200, 'type' => 'json'));
}
if ($mirrorOnEventRequested && !empty($target_id_list)) {
$first_attribute_id = $target_id_list[0]; // We consider that all attributes to be tagged are contained in the same event.
$this->loadModel('Attribute');
$attribute = $this->Attribute->fetchAttributeSimple($this->Auth->user(), array('conditions' => array('Attribute.id' => $first_attribute_id), 'flatten' => 1));
if (!empty($attribute['Attribute']['event_id'])) {
$event_id = $attribute['Attribute']['event_id'];
} else {
return new CakeResponse(array('body' => json_encode(array('saved' => false, 'errors' => __('Failed to parse request. Could not fetch attribute'))), 'status' => 200, 'type' => 'json'));
}
}
$result = "";
if (!is_array($cluster_ids)) { // in case we only want to attach 1
$cluster_ids = array($cluster_ids);
@ -565,6 +553,9 @@ class GalaxiesController extends AppController
foreach ($cluster_ids as $cluster_id) {
foreach ($target_id_list as $target_id) {
$result = $this->Galaxy->attachCluster($this->Auth->user(), $target_type, $target_id, $cluster_id, $local);
if ($mirrorOnEventRequested) {
$result = $result && $this->Galaxy->attachCluster($this->Auth->user(), 'event', $event_id, $cluster_id, $local);
}
}
}
if ($this->request->is('ajax')) {
@ -574,6 +565,7 @@ class GalaxiesController extends AppController
$this->redirect($this->referer());
}
} else {
$this->set('local', $local);
$this->set('target_id', $target_id);
$this->set('target_type', $target_type);
$this->layout = false;
@ -602,29 +594,32 @@ class GalaxiesController extends AppController
public function showGalaxies($id, $scope = 'event')
{
$this->layout = 'ajax';
$this->set('scope', $scope);
if ($scope == 'event') {
if ($scope === 'event') {
$this->loadModel('Event');
$object = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id, 'metadata' => 1));
if (empty($object)) {
throw new MethodNotAllowedException('Invalid event.');
throw new NotFoundException('Invalid event.');
}
$this->set('object', $object[0]);
} elseif ($scope == 'attribute') {
} elseif ($scope === 'attribute') {
$this->loadModel('Attribute');
$object = $this->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id), 'flatten' => 1));
if (empty($object)) {
throw new MethodNotAllowedException('Invalid attribute.');
throw new NotFoundException('Invalid attribute.');
}
$object[0] = $this->Attribute->Event->massageTags($this->Auth->user(), $object[0], 'Attribute');
} elseif ($scope == 'tag_collection') {
} elseif ($scope === 'tag_collection') {
$this->loadModel('TagCollection');
$object = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $id)));
if (empty($object)) {
throw new MethodNotAllowedException('Invalid Tag Collection.');
throw new NotFoundException('Invalid Tag Collection.');
}
} else {
throw new NotFoundException("Invalid scope.");
}
$this->layout = false;
$this->set('scope', $scope);
$this->set('object', $object[0]);
$this->render('/Events/ajax/ajaxGalaxies');
}

View File

@ -72,8 +72,6 @@ class GalaxyClusterRelationsController extends AppController
$this->paginate['contain'] = array('SharingGroup', 'SourceCluster' => ['Org', 'Orgc'], 'TargetCluster', 'GalaxyClusterRelationTag' => array('Tag'));
$relations = $this->paginate();
$relations = $this->GalaxyClusterRelation->removeNonAccessibleTargetCluster($this->Auth->user(), $relations);
$this->loadModel('SharingGroup');
$sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
unset($distributionLevels[5]);

View File

@ -9,30 +9,30 @@ class GalaxyClustersController extends AppController
public $components = array('Session', 'RequestHandler');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
'recursive' => -1,
'order' => array(
'GalaxyCluster.version' => 'DESC',
'GalaxyCluster.value' => 'ASC'
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
'recursive' => -1,
'order' => array(
'GalaxyCluster.version' => 'DESC',
'GalaxyCluster.value' => 'ASC'
),
'contain' => array(
'Tag' => array(
'fields' => array('Tag.id'),
/*
'EventTag' => array(
'fields' => array('EventTag.event_id')
),
'AttributeTag' => array(
'fields' => array('AttributeTag.event_id', 'AttributeTag.attribute_id')
)
*/
),
'contain' => array(
'Tag' => array(
'fields' => array('Tag.id'),
/*
'EventTag' => array(
'fields' => array('EventTag.event_id')
),
'AttributeTag' => array(
'fields' => array('AttributeTag.event_id', 'AttributeTag.attribute_id')
)
*/
),
'GalaxyElement' => array(
'conditions' => array('GalaxyElement.key' => 'synonyms'),
'fields' => array('value')
),
)
'GalaxyElement' => array(
'conditions' => array('GalaxyElement.key' => 'synonyms'),
'fields' => array('value')
),
)
);
public function index($galaxyId)
@ -155,7 +155,7 @@ class GalaxyClustersController extends AppController
$this->set('custom_cluster_count', $customClusterCount);
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/index');
}
}
@ -165,7 +165,6 @@ class GalaxyClustersController extends AppController
*/
public function view($id)
{
$id = $this->Toolbox->findIdByUuid($this->GalaxyCluster, $id);
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors=true, $full=true);
$tag = $this->GalaxyCluster->Tag->find('first', array(
'conditions' => array(
@ -181,29 +180,30 @@ class GalaxyClustersController extends AppController
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($cluster, $this->response->type());
} else {
$clusters = [$cluster];
$this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
$cluster = $clusters[0];
$cluster = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $cluster);
$this->set('id', $id);
$this->set('galaxy', ['Galaxy' => $cluster['GalaxyCluster']['Galaxy']]);
$this->set('galaxy_id', $cluster['GalaxyCluster']['galaxy_id']);
$this->set('cluster', $cluster);
$this->set('defaultCluster', $cluster['GalaxyCluster']['default']);
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
$newVersionAvailable = $cluster['GalaxyCluster']['extended_from']['GalaxyCluster']['version'] > $cluster['GalaxyCluster']['extends_version'];
} else {
$newVersionAvailable = false;
}
$this->set('newVersionAvailable', $newVersionAvailable);
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
$this->set('distributionLevels', $distributionLevels);
if (!$cluster['GalaxyCluster']['default'] && !$cluster['GalaxyCluster']['published'] && $cluster['GalaxyCluster']['orgc_id'] == $this->Auth->user()['org_id']) {
$this->Flash->warning(__('This cluster is not published. Users will not be able to use it'));
}
}
$clusters = [$cluster];
$this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
$cluster = $clusters[0];
$cluster = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $cluster);
$this->set('id', $cluster['GalaxyCluster']['id']);
$this->set('galaxy', ['Galaxy' => $cluster['GalaxyCluster']['Galaxy']]);
$this->set('galaxy_id', $cluster['GalaxyCluster']['galaxy_id']);
$this->set('cluster', $cluster);
$this->set('defaultCluster', $cluster['GalaxyCluster']['default']);
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
$newVersionAvailable = $cluster['GalaxyCluster']['extended_from']['GalaxyCluster']['version'] > $cluster['GalaxyCluster']['extends_version'];
} else {
$newVersionAvailable = false;
}
$this->set('newVersionAvailable', $newVersionAvailable);
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
$this->set('distributionLevels', $distributionLevels);
if (!$cluster['GalaxyCluster']['default'] && !$cluster['GalaxyCluster']['published'] && $cluster['GalaxyCluster']['orgc_id'] == $this->Auth->user()['org_id']) {
$this->Flash->warning(__('This cluster is not published. Users will not be able to use it'));
}
$this->set('title_for_layout', __('Galaxy cluster %s', $cluster['GalaxyCluster']['value']));
}
/**
@ -545,162 +545,32 @@ class GalaxyClustersController extends AppController
}
}
public function attachToEvent($event_id, $tag_name)
{
$this->loadModel('Event');
$this->Event->id = $event_id;
$this->Event->recursive = -1;
$event = $this->Event->read(array(), $event_id);
if (empty($event)) {
throw new MethodNotAllowedException('Invalid Event.');
}
if (!$this->_isSiteAdmin() && !$this->userRole['perm_sync']) {
if (!$this->userRole['perm_tagger'] || ($this->Auth->user('org_id') !== $event['Event']['org_id'] && $this->Auth->user('org_id') !== $event['Event']['orgc_id'])) {
throw new MethodNotAllowedException('Invalid Event.');
}
}
$tag = $this->Event->EventTag->Tag->find('first', array('conditions' => array('Tag.name' => $tag_name), 'recursive' => -1));
if (empty($tag)) {
$this->Event->EventTag->Tag->create();
$this->Event->EventTag->Tag->save(array('name' => $tag_name, 'colour' => '#0088cc', 'exportable' => 1));
$tag_id = $this->Event->EventTag->Tag->id;
} else {
$tag_id = $tag['Tag']['id'];
}
$existingEventTag = $this->Event->EventTag->find('first', array('conditions' => array('EventTag.tag_id' => $tag_id, 'EventTag.event_id' => $event_id), 'recursive' => -1));
if (empty($existingEventTag)) {
$cluster = $this->GalaxyCluster->find('first', array(
'recursive' => -1,
'conditions' => array('GalaxyCluster.tag_name' => $existingEventTag['Tag']['name'])
));
$this->Event->EventTag->create();
$this->Event->EventTag->save(array('EventTag.tag_id' => $tag_id, 'EventTag.event_id' => $event_id));
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $event_id,
'email' => $this->Auth->user('email'),
'action' => 'galaxy',
'title' => 'Attached ' . $cluster['GalaxyCluster']['value'] . ' (' . $cluster['GalaxyCluster']['id'] . ') to event (' . $event_id . ')',
'change' => ''
));
$event['Event']['published'] = 0;
$date = new DateTime();
$event['Event']['timestamp'] = $date->getTimestamp();
$this->Event->save($event);
$this->Flash->success('Galaxy attached.');
} else {
$this->Flash->error('Galaxy already attached.');
}
$this->redirect($this->referer());
}
public function detach($target_id, $target_type, $tag_id)
{
$this->loadModel('Event');
if ($target_type == 'attribute') {
$attribute = $this->Event->Attribute->find('first', array(
'recursive' => -1,
'fields' => array('id', 'event_id'),
'conditions' => array('Attribute.id' => $target_id)
));
if (empty($attribute)) {
throw new MethodNotAllowedException('Invalid Attribute.');
}
$event_id = $attribute['Attribute']['event_id'];
} elseif ($target_type == 'event') {
$event_id = $target_id;
} elseif ($target_type === 'tag_collection') {
// pass
} else {
throw new MethodNotAllowedException('Invalid options');
if ($this->request->is('ajax') && $this->request->is('get')) {
$this->set('url', Router::url());
return $this->render('/Elements/emptyForm', false);
}
if ($target_type === 'tag_collection') {
$tag_collection = $this->GalaxyCluster->Tag->TagCollectionTag->TagCollection->fetchTagCollection($this->Auth->user(), array(
'conditions' => array('TagCollection.id' => $target_id),
'contain' => array('Organisation', 'TagCollectionTag' => array('Tag'))
));
if (empty($tag_collection)) {
throw new MethodNotAllowedException('Invalid Tag Collection');
}
$tag_collection = $tag_collection[0];
if (!$this->_isSiteAdmin()) {
if (!$this->userRole['perm_tag_editor'] || $this->Auth->user('org_id') !== $tag_collection['TagCollection']['org_id']) {
throw new MethodNotAllowedException('Invalid Tag Collection');
}
}
} else {
$this->Event->id = $event_id;
$this->Event->recursive = -1;
$event = $this->Event->read(array(), $event_id);
if (empty($event)) {
throw new MethodNotAllowedException('Invalid Event.');
}
if (!$this->_isSiteAdmin() && !$this->userRole['perm_sync']) {
if (!$this->userRole['perm_tagger'] || ($this->Auth->user('org_id') !== $event['Event']['org_id'] && $this->Auth->user('org_id') !== $event['Event']['orgc_id'])) {
throw new MethodNotAllowedException('Invalid Event.');
}
}
}
$this->request->allowMethod(['post']);
if ($target_type == 'attribute') {
$existingTargetTag = $this->Event->Attribute->AttributeTag->find('first', array(
'conditions' => array('AttributeTag.tag_id' => $tag_id, 'AttributeTag.attribute_id' => $target_id),
'recursive' => -1,
'contain' => array('Tag')
));
} elseif ($target_type == 'event') {
$existingTargetTag = $this->Event->EventTag->find('first', array(
'conditions' => array('EventTag.tag_id' => $tag_id, 'EventTag.event_id' => $target_id),
'recursive' => -1,
'contain' => array('Tag')
));
} elseif ($target_type == 'tag_collection') {
$existingTargetTag = $this->GalaxyCluster->Tag->TagCollectionTag->find('first', array(
'conditions' => array('TagCollectionTag.tag_id' => $tag_id, 'TagCollectionTag.tag_collection_id' => $target_id),
'recursive' => -1,
'contain' => array('Tag')
));
}
if (empty($existingTargetTag)) {
$this->Flash->error('Galaxy not attached.');
} else {
$cluster = $this->GalaxyCluster->find('first', array(
'recursive' => -1,
'conditions' => array('GalaxyCluster.tag_name' => $existingTargetTag['Tag']['name'])
));
if ($target_type == 'event') {
$result = $this->Event->EventTag->delete($existingTargetTag['EventTag']['id']);
} elseif ($target_type == 'attribute') {
$result = $this->Event->Attribute->AttributeTag->delete($existingTargetTag['AttributeTag']['id']);
} elseif ($target_type == 'tag_collection') {
$result = $this->GalaxyCluster->Tag->TagCollectionTag->delete($existingTargetTag['TagCollectionTag']['id']);
}
if ($result) {
$event['Event']['published'] = 0;
$date = new DateTime();
$event['Event']['timestamp'] = $date->getTimestamp();
$this->Event->save($event);
$this->Flash->success('Galaxy successfully detached.');
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => ucfirst($target_type),
'model_id' => $target_id,
'email' => $this->Auth->user('email'),
'action' => 'galaxy',
'title' => 'Detached ' . $cluster['GalaxyCluster']['value'] . ' (' . $cluster['GalaxyCluster']['id'] . ') from ' . $target_type . ' (' . $target_id . ')',
'change' => ''
));
try {
$this->GalaxyCluster->Galaxy->detachClusterByTagId($this->Auth->user(), $target_id, $target_type, $tag_id);
} catch (NotFoundException $e) {
if (!$this->request->is('ajax')) {
$this->Flash->error($e->getMessage());
} else {
$this->Flash->error('Could not detach galaxy from event.');
throw $e;
}
}
$message = __('Galaxy successfully detached.');
if ($this->request->is('ajax')) {
return $this->RestResponse->viewData(['saved' => true, 'check_publish' => true, 'success' => $message], 'json');
}
$this->Flash->success($message);
$this->redirect($this->referer());
}

View File

@ -53,7 +53,7 @@ class GalaxyElementsController extends AppController
$this->set('JSONElements', $expanded);
}
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/index');
}
}
@ -82,7 +82,7 @@ class GalaxyElementsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
} else {
$this->layout = 'ajax';
$this->layout = false;
$this->set('elementId', $elementId);
$this->render('ajax/delete');
}
@ -110,7 +110,7 @@ class GalaxyElementsController extends AppController
}
$this->set('clusterId', $clusterId);
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/flattenJson');
}
}

View File

@ -16,6 +16,15 @@ class JobsController extends AppController
),
);
public function beforeFilter()
{
parent::beforeFilter();
if ($this->request->action === 'getGenerateCorrelationProgress') {
$this->Security->doNotGenerateToken = true;
}
}
public function index($queue = false)
{
if (!Configure::read('MISP.background_jobs')) {
@ -24,9 +33,9 @@ class JobsController extends AppController
$this->loadModel('Server');
$issueCount = 0;
$workers = $this->Server->workerDiagnostics($issueCount);
$queues = array('email', 'default', 'cache', 'prio', 'update');
$queues = ['email', 'default', 'cache', 'prio', 'update'];
if ($queue && in_array($queue, $queues, true)) {
$this->paginate['conditions'] = array('Job.worker' => $queue);
$this->paginate['conditions'] = ['Job.worker' => $queue];
}
$jobs = $this->paginate();
foreach ($jobs as &$job) {
@ -37,9 +46,9 @@ class JobsController extends AppController
$job['Job']['job_status'] = 'Unknown';
$job['Job']['failed'] = null;
}
if(Configure::read('SimpleBackgroundJobs.enabled')){
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$job['Job']['worker_status'] = true;
}else{
} else {
$job['Job']['worker_status'] = isset($workers[$job['Job']['worker']]) && $workers[$job['Job']['worker']]['ok'];
}
}
@ -78,20 +87,27 @@ class JobsController extends AppController
}
}
public function getGenerateCorrelationProgress($id)
public function getGenerateCorrelationProgress($ids)
{
$job = $this->Job->find('first', [
'fields' => ['progress', 'process_id'],
'conditions' => ['id' => $id],
$this->_closeSession();
$ids = explode(",", $ids);
$jobs = $this->Job->find('all', [
'fields' => ['id', 'progress', 'process_id'],
'conditions' => ['id' => $ids],
'recursive' => -1,
]);
if (!$job) {
throw new NotFoundException("Job with ID `$id` not found");
if (empty($jobs)) {
throw new NotFoundException('No jobs found');
}
$output = [];
foreach ($jobs as $job) {
$output[$job['Job']['id']] = [
'job_status' => $this->__getJobStatus($job['Job']['process_id']),
'progress' => (int)$job['Job']['progress'],
];
}
$output = [
'job_status' => $this->__getJobStatus($job['Job']['process_id']),
'progress' => (int)$job['Job']['progress'],
];
return $this->RestResponse->viewData($output, 'json');
}

View File

@ -30,17 +30,17 @@ class LogsController extends AppController
public function admin_index()
{
$paramArray = array('id', 'title', 'created', 'model', 'model_id', 'action', 'user_id', 'change', 'email', 'org', 'description', 'ip');
$filterData = array(
'request' => $this->request,
'named_params' => $this->params['named'],
'paramArray' => $paramArray,
'ordered_url_params' => func_get_args()
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
unset($filterData);
if ($this->_isRest()) {
$paramArray = array('id', 'title', 'created', 'model', 'model_id', 'action', 'user_id', 'change', 'email', 'org', 'description', 'ip');
$filterData = array(
'request' => $this->request,
'named_params' => $this->params['named'],
'paramArray' => $paramArray,
'ordered_url_params' => func_get_args()
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
unset($filterData);
if ($filters === false) {
return $exception;
}
@ -100,6 +100,9 @@ class LogsController extends AppController
if (isset($this->params['named']['filter']) && in_array($this->params['named']['filter'], array_keys($validFilters))) {
$this->paginate['conditions']['Log.action'] = $validFilters[$this->params['named']['filter']]['values'];
}
foreach ($filters as $key => $value) {
$this->paginate['conditions']["Log.$key"] = $value;
}
$this->set('validFilters', $validFilters);
$this->set('filter', isset($this->params['named']['filter']) ? $this->params['named']['filter'] : false);
$this->set('list', $this->paginate());
@ -389,6 +392,7 @@ class LogsController extends AppController
'Galaxy',
'GalaxyCluster',
'GalaxyClusterRelation',
'Workflow',
];
sort($models);
$models = array('' => 'ALL') + $this->_arrayToValuesIndexArray($models);

View File

@ -168,7 +168,7 @@ class NoticelistsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException('This action is available via AJAX only.');
}
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/getToggleField');
}

View File

@ -9,10 +9,10 @@ class ObjectReferencesController extends AppController
public $components = array('RequestHandler', 'Session');
public $paginate = array(
'limit' => 20,
'order' => array(
'ObjectReference.id' => 'desc'
),
'limit' => 20,
'order' => array(
'ObjectReference.id' => 'desc'
),
);
public function add($objectId = false)
@ -23,7 +23,7 @@ class ObjectReferencesController extends AppController
}
}
if (empty($objectId)) {
throw new MethodNotAllowedException('No object defined.');
throw new NotFoundException('No object defined.');
}
if (Validation::uuid($objectId)) {
$conditions = ['Object.uuid' => $objectId];
@ -91,65 +91,63 @@ class ObjectReferencesController extends AppController
} else {
if ($this->_isRest()) {
return $this->RestResponse->describe('ObjectReferences', 'add', false, $this->response->type());
} else {
$events = $this->ObjectReference->Object->Event->find('all', array(
'conditions' => array(
'OR' => array(
'Event.id' => $object['Event']['id'],
'AND' => array(
'Event.uuid' => $object['Event']['extends_uuid'],
$this->ObjectReference->Object->Event->createEventConditions($this->Auth->user())
)
),
}
$events = $this->ObjectReference->Object->Event->find('all', array(
'conditions' => array(
'OR' => array(
'Event.id' => $object['Event']['id'],
'AND' => array(
'Event.uuid' => $object['Event']['extends_uuid'],
$this->ObjectReference->Object->Event->createEventConditions($this->Auth->user())
)
),
'recursive' => -1,
'fields' => array('Event.id'),
'contain' => array(
),
'recursive' => -1,
'fields' => array('Event.id'),
'contain' => array(
'Attribute' => array(
'conditions' => array('Attribute.deleted' => 0, 'Attribute.object_id' => 0),
'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids')
),
'Object' => array(
'conditions' => array('NOT' => array('Object.id' => $object['Object']['id']), 'Object.deleted' => 0),
'fields' => array('Object.id', 'Object.uuid', 'Object.name', 'Object.meta-category'),
'Attribute' => array(
'conditions' => array('Attribute.deleted' => 0, 'Attribute.object_id' => 0),
'conditions' => array('Attribute.deleted' => 0),
'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids')
),
'Object' => array(
'conditions' => array('NOT' => array('Object.id' => $object['Object']['id']), 'Object.deleted' => 0),
'fields' => array('Object.id', 'Object.uuid', 'Object.name', 'Object.meta-category'),
'Attribute' => array(
'conditions' => array('Attribute.deleted' => 0),
'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids')
)
)
)
));
if (!empty($events)) {
$event = $events[0];
}
for ($i=1; $i < count($events); $i++) {
$event['Attribute'] = array_merge($event['Attribute'], $events[$i]['Attribute']);
$event['Object'] = array_merge($event['Object'], $events[$i]['Object']);
}
$toRearrange = array('Attribute', 'Object');
foreach ($toRearrange as $d) {
if (!empty($event[$d])) {
$temp = array();
foreach ($event[$d] as $data) {
$temp[$data['uuid']] = $data;
}
$event[$d] = $temp;
}
}
$this->loadModel('ObjectRelationship');
$relationships = $this->ObjectRelationship->find('column', array(
'recursive' => -1,
'fields' => ['name'],
));
$relationships = array_combine($relationships, $relationships);
$relationships['custom'] = 'custom';
ksort($relationships);
$this->set('relationships', $relationships);
$this->set('event', $event);
$this->set('objectId', $object['Object']['id']);
$this->layout = 'ajax';
$this->render('ajax/add');
)
));
$event = $events[0];
for ($i = 1; $i < count($events); $i++) {
$event['Attribute'] = array_merge($event['Attribute'], $events[$i]['Attribute']);
$event['Object'] = array_merge($event['Object'], $events[$i]['Object']);
}
$toRearrange = array('Attribute', 'Object');
foreach ($toRearrange as $d) {
if (!empty($event[$d])) {
$temp = array();
foreach ($event[$d] as $data) {
$temp[$data['uuid']] = $data;
}
$event[$d] = $temp;
}
}
$this->loadModel('ObjectRelationship');
$relationships = $this->ObjectRelationship->find('column', array(
'recursive' => -1,
'fields' => ['name'],
));
$relationships = array_combine($relationships, $relationships);
$relationships['custom'] = 'custom';
ksort($relationships);
$this->set('relationships', $relationships);
$this->set('event', $event);
$this->set('objectId', $object['Object']['id']);
$this->layout = false;
$this->render('ajax/add');
}
}

View File

@ -19,7 +19,7 @@ class ObjectTemplateElementsController extends AppController
$this->paginate['conditions'] = array('ObjectTemplateElement.object_template_id' => $id);
$elements = $this->paginate();
$this->set('list', $elements);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/view_elements');
}
}

View File

@ -9,18 +9,28 @@ class ObjectTemplatesController extends AppController
public $components = array('RequestHandler', 'Session');
public $paginate = array(
'limit' => 60,
'order' => array(
'Object.id' => 'desc'
),
'contain' => array(
'Organisation' => array('fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'))
),
'recursive' => -1
'limit' => 60,
'order' => array(
'Object.id' => 'desc'
),
'contain' => array(
'Organisation' => array('fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'))
),
'recursive' => -1
);
public function objectMetaChoice($event_id)
public function beforeFilter()
{
parent::beforeFilter();
if (in_array($this->request->action, ['objectMetaChoice', 'objectChoice'], true)) {
$this->Security->doNotGenerateToken = true;
}
}
public function objectMetaChoice($eventId)
{
session_abort();
$metas = $this->ObjectTemplate->find('column', array(
'conditions' => array('ObjectTemplate.active' => 1),
'fields' => array('ObjectTemplate.meta-category'),
@ -28,7 +38,6 @@ class ObjectTemplatesController extends AppController
'unique' => true,
));
$eventId = h($event_id);
$items = [[
'name' => __('All Objects'),
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/0"
@ -36,7 +45,7 @@ class ObjectTemplatesController extends AppController
foreach ($metas as $meta) {
$items[] = array(
'name' => $meta,
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/" . h($meta)
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/$meta",
);
}
@ -49,7 +58,8 @@ class ObjectTemplatesController extends AppController
public function objectChoice($event_id, $category=false)
{
$this->ObjectTemplate->populateIfEmpty($this->Auth->user());
$user = $this->_closeSession();
$this->ObjectTemplate->populateIfEmpty($user);
$conditions = array('ObjectTemplate.active' => 1);
if ($category !== false && $category !== "0") {
$conditions['meta-category'] = $category;
@ -158,7 +168,7 @@ class ObjectTemplatesController extends AppController
'conditions' => array('ObjectTemplateElement.object_template_id' => $id)
));
$this->set('list', $elements);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/view_elements');
}
@ -293,7 +303,7 @@ class ObjectTemplatesController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException('This action is available via AJAX only.');
}
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/getToggleField');
}

View File

@ -356,19 +356,20 @@ class ObjectsController extends AppController
public function edit($id, $update_template_available=false, $onlyAddNewAttribute=false)
{
$object = $this->MispObject->fetchObjects($this->Auth->user(), array(
$user = $this->Auth->user();
$object = $this->MispObject->fetchObjects($user, array(
'conditions' => $this->__objectIdToConditions($id),
));
if (empty($object)) {
throw new NotFoundException(__('Invalid object.'));
}
$object = $object[0];
$event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $object['Event']['id']);
$event = $this->MispObject->Event->fetchSimpleEvent($user, $object['Event']['id']);
if (!$this->__canModifyEvent($event)) {
throw new ForbiddenException(__('Insufficient permissions to edit this object.'));
}
if (!$this->_isRest()) {
$this->MispObject->Event->insertLock($this->Auth->user(), $object['Event']['id']);
$this->MispObject->Event->insertLock($user, $object['Event']['id']);
}
if (!empty($object['Object']['template_uuid']) && !empty($object['Object']['template_version'])) {
$template = $this->MispObject->ObjectTemplate->find('first', array(
@ -425,7 +426,7 @@ class ObjectsController extends AppController
unset($this->request->data['Object']);
}
$objectToSave = $this->MispObject->attributeCleanup($this->request->data);
$objectToSave = $this->MispObject->deltaMerge($object, $objectToSave, $onlyAddNewAttribute, $this->Auth->user());
$objectToSave = $this->MispObject->deltaMerge($object, $objectToSave, $onlyAddNewAttribute, $user);
$error_message = __('Object could not be saved.');
$savedObject = array();
if (!is_numeric($objectToSave)) {
@ -435,10 +436,10 @@ class ObjectsController extends AppController
}
$error_message = __('Object could not be saved.') . PHP_EOL . implode(PHP_EOL, $object_validation_errors);
} else {
$savedObject = $this->MispObject->fetchObjects($this->Auth->user(), array('conditions' => array('Object.id' => $object['Object']['id'])));
$savedObject = $this->MispObject->fetchObjects($user, array('conditions' => array('Object.id' => $object['Object']['id'])));
if (isset($this->request->data['deleted']) && $this->request->data['deleted']) {
$this->MispObject->deleteObject($savedObject[0], $hard=false, $unpublish=false);
$savedObject = $this->MispObject->fetchObjects($this->Auth->user(), array('conditions' => array('Object.id' => $object['Object']['id']))); // make sure the object is deleted
$savedObject = $this->MispObject->fetchObjects($user, array('conditions' => array('Object.id' => $object['Object']['id']))); // make sure the object is deleted
}
}
// we pre-validate the attributes before we create an object at this point
@ -480,15 +481,15 @@ class ObjectsController extends AppController
$enabledRows = array();
$this->request->data['Object'] = $object['Object'];
foreach ($template['ObjectTemplateElement'] as $k => $element) {
foreach ($object['Attribute'] as $k2 => $attribute) {
if ($attribute['object_relation'] == $element['object_relation']) {
foreach ($object['Attribute'] as $attribute) {
if ($attribute['object_relation'] === $element['object_relation']) {
$enabledRows[] = $k;
$this->request->data['Attribute'][$k] = $attribute;
if (!empty($element['values_list'])) {
$this->request->data['Attribute'][$k]['value_select'] = $attribute['value'];
} else {
if (!empty($element['sane_default'])) {
if (in_array($attribute['value'], $element['sane_default'])) {
if (in_array($attribute['value'], $element['sane_default'], true)) {
$this->request->data['Attribute'][$k]['value_select'] = $attribute['value'];
} else {
$this->request->data['Attribute'][$k]['value_select'] = 'Enter value manually';
@ -500,7 +501,7 @@ class ObjectsController extends AppController
}
}
$this->set('enabledRows', $enabledRows);
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($user);
$this->set('distributionData', $distributionData);
$this->set('event', $event);
$this->set('ajax', false);
@ -573,21 +574,21 @@ class ObjectsController extends AppController
public function fetchViewValue($id, $field = null)
{
$validFields = array('timestamp', 'comment', 'distribution', 'first_seen', 'last_seen');
if (!isset($field) || !in_array($field, $validFields)) {
if (!isset($field) || !in_array($field, $validFields, true)) {
throw new MethodNotAllowedException('Invalid field requested.');
}
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException('This function can only be accessed via AJAX.');
}
$params = array(
'conditions' => array('Object.id' => $id),
'fields' => array('id', 'distribution', 'event_id', $field),
'contain' => array(
'Event' => array(
'fields' => array('distribution', 'id', 'org_id'),
)
),
'flatten' => 1
'conditions' => array('Object.id' => $id),
'fields' => array('id', 'distribution', 'event_id', $field),
'contain' => array(
'Event' => array(
'fields' => array('distribution', 'id', 'org_id'),
)
),
'flatten' => 1
);
$object = $this->MispObject->fetchObjectSimple($this->Auth->user(), $params);
if (empty($object)) {
@ -595,11 +596,12 @@ class ObjectsController extends AppController
}
$object = $object[0];
$result = $object['Object'][$field];
if ($field == 'distribution') {
$result=$this->MispObject->shortDist[$result];
if ($field === 'distribution') {
$result = $this->MispObject->shortDist[$result];
}
$this->set('value', $result);
$this->layout = 'ajax';
$this->set('field', $field);
$this->layout = false;
$this->render('ajax/objectViewFieldForm');
}
@ -631,7 +633,7 @@ class ObjectsController extends AppController
if (!$this->__canModifyEvent($object)) {
throw new NotFoundException(__('Invalid object'));
}
$this->layout = 'ajax';
$this->layout = false;
if ($field == 'distribution') {
$distributionLevels = $this->MispObject->shortDist;
unset($distributionLevels[4]);
@ -645,10 +647,9 @@ class ObjectsController extends AppController
// Construct a template with valid object attributes to add to an object
public function quickFetchTemplateWithValidObjectAttributes($id)
{
$fields = array('template_uuid', 'template_version', 'id');
$params = array(
'conditions' => array('Object.id' => $id),
'fields' => $fields,
'fields' => array('template_uuid', 'template_version', 'id'),
'flatten' => 1,
);
// fetchObjects restrict access based on user
@ -663,11 +664,10 @@ class ObjectsController extends AppController
$object = $object[0];
}
// get object attributes already set
$objectRelation = array();
foreach($object['Attribute'] as $attr) {
$objectRelation[$attr['object_relation']] = 1;
$existsObjectRelation = array();
foreach ($object['Attribute'] as $attr) {
$existsObjectRelation[$attr['object_relation']] = true;
}
$objectRelation = array_keys($objectRelation);
// get object attribute defined in the object's template
$template = $this->MispObject->ObjectTemplate->find('first', array(
'conditions' => array(
@ -686,8 +686,8 @@ class ObjectsController extends AppController
}
}
// unset object invalid object attribute
foreach($template['ObjectTemplateElement'] as $i => $objAttr) {
if (in_array($objAttr['object_relation'], $objectRelation) && !$objAttr['multiple']) {
foreach ($template['ObjectTemplateElement'] as $i => $objAttr) {
if (isset($existsObjectRelation[$objAttr['object_relation']]) && !$objAttr['multiple']) {
unset($template['ObjectTemplateElement'][$i]);
}
}
@ -727,10 +727,9 @@ class ObjectsController extends AppController
if (!isset($fieldName)) {
throw new MethodNotAllowedException('No field requested.');
}
$fields = array('template_uuid', 'template_version', 'id', 'event_id');
$params = array(
'conditions' => array('Object.id' => $id),
'fields' => $fields,
'fields' => array('template_uuid', 'template_version', 'id', 'event_id'),
'flatten' => 1,
'contain' => array(
'Event'
@ -740,9 +739,8 @@ class ObjectsController extends AppController
$object = $this->MispObject->fetchObjects($this->Auth->user(), $params);
if (empty($object)) {
throw new NotFoundException(__('Invalid object'));
} else {
$object = $object[0];
}
$object = $object[0];
if (!$this->__canModifyEvent($object)) {
throw new ForbiddenException(__('You do not have permission to do that.'));
}
@ -763,29 +761,34 @@ class ObjectsController extends AppController
throw new NotFoundException(__('Invalid template'));
}
if (empty($template['ObjectTemplateElement'])) {
throw new NotFoundException(__('Invalid fields') . ' `' . h($fieldName) . '`');
throw new NotFoundException(__('Invalid field `%s`', h($fieldName)));
}
// check if fields can be added
foreach($object['Attribute'] as $i => $objAttr) {
foreach ($object['Attribute'] as $objAttr) {
$objectAttrFromTemplate = $template['ObjectTemplateElement'][0];
if ($objAttr['object_relation'] == $fieldName && !$objectAttrFromTemplate['multiple']) {
if ($objAttr['object_relation'] === $fieldName && !$objectAttrFromTemplate['multiple']) {
throw new NotFoundException(__('Invalid field'));
}
}
$template = $this->MispObject->prepareTemplate($template, $object);
$this->layout = 'ajax';
$this->layout = false;
$this->set('object', $object['Object']);
$template_element = $template['ObjectTemplateElement'][0];
unset($template_element['value']); // avoid filling if multiple
$this->set('template_element', $template_element);
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
$distributionData = $this->MispObject->Attribute->fetchDistributionData($this->Auth->user());
$this->set('distributionData', $distributionData);
$info = array();
foreach ($distributionData['levels'] as $key => $value) {
$info['distribution'][$key] = array('key' => $value, 'desc' => $this->MispObject->Event->Attribute->distributionDescriptions[$key]['formdesc']);
$info = ['category' => [], 'distribution' => []];
foreach ($this->MispObject->Attribute->categoryDefinitions as $key => $value) {
$info['category'][$key] = isset($value['formdesc']) ? $value['formdesc'] : $value['desc'];
}
$this->set('info', $info);
foreach ($this->MispObject->Attribute->distributionLevels as $key => $value) {
$info['distribution'][$key] = $this->MispObject->Attribute->distributionDescriptions[$key]['formdesc'];
}
$this->set('fieldDesc', $info);
$this->render('ajax/quickAddAttributeForm');
} else if ($this->request->is('post') || $this->request->is('put')) {
return $this->edit($this->request->data['Object']['id'], false, true);
@ -1126,16 +1129,17 @@ class ObjectsController extends AppController
$this->set('unmapped', $unmappedAttributes);
}
function proposeObjectsFromAttributes($event_id, $selected_attributes='[]')
public function proposeObjectsFromAttributes($eventId, $selectedAttributes='[]')
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action can only be reached via AJAX.'));
}
$selected_attributes = json_decode($selected_attributes, true);
$res = $this->MispObject->validObjectsFromAttributeTypes($this->Auth->user(), $event_id, $selected_attributes);
$potential_templates = $res['templates'];
$attribute_types = $res['types'];
usort($potential_templates, function($a, $b) {
$selectedAttributes = $this->_jsonDecode($selectedAttributes);
$res = $this->MispObject->validObjectsFromAttributeTypes($this->Auth->user(), $eventId, $selectedAttributes);
$potentialTemplates = $res['templates'];
$attributeTypes = $res['types'];
usort($potentialTemplates, function($a, $b) {
if ($a['ObjectTemplate']['id'] == $b['ObjectTemplate']['id']) {
return 0;
} else if (is_array($a['ObjectTemplate']['compatibility']) && is_array($b['ObjectTemplate']['compatibility'])) {
@ -1148,13 +1152,17 @@ class ObjectsController extends AppController
return count($a['ObjectTemplate']['invalidTypes']) > count($b['ObjectTemplate']['invalidTypes']) ? 1 : -1;
}
});
$this->set('potential_templates', $potential_templates);
$this->set('selected_types', $attribute_types);
$this->set('event_id', $event_id);
$this->set('potential_templates', $potentialTemplates);
$this->set('selected_types', $attributeTypes);
$this->set('event_id', $eventId);
}
public function groupAttributesIntoObject($event_id, $selected_template, $selected_attribute_ids='[]')
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action can only be reached via AJAX.'));
}
$event = $this->MispObject->Event->find('first', array(
'recursive' => -1,
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.user_id', 'Event.publish_timestamp'),
@ -1167,9 +1175,6 @@ class ObjectsController extends AppController
throw new ForbiddenException(__('You do not have permission to do that.'));
}
$hard_delete_attribute = $event['Event']['publish_timestamp'] == 0;
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action can only be reached via AJAX.'));
}
if ($this->request->is('post')) {
$template = $this->MispObject->ObjectTemplate->find('first', array(
'recursive' => -1,
@ -1179,22 +1184,22 @@ class ObjectsController extends AppController
throw new NotFoundException(__('Invalid template.'));
}
$distribution = $this->request->data['Object']['distribution'];
$sharing_group_id = $this->request->data['Object']['sharing_group_id'];
$sharingGroupId = $this->request->data['Object']['sharing_group_id'] ?? 0;
$comment = $this->request->data['Object']['comment'];
$selected_attribute_ids = json_decode($this->request->data['Object']['selectedAttributeIds'], true);
$selected_object_relation_mapping = json_decode($this->request->data['Object']['selectedObjectRelationMapping'], true);
$selected_attribute_ids = $this->_jsonDecode($this->request->data['Object']['selectedAttributeIds']);
$selected_object_relation_mapping = $this->_jsonDecode($this->request->data['Object']['selectedObjectRelationMapping']);
if ($distribution == 4) {
$sg = $this->MispObject->SharingGroup->fetchSG($sharing_group_id, $this->Auth->user());
$sg = $this->MispObject->SharingGroup->fetchSG($sharingGroupId, $this->Auth->user());
if (empty($sg)) {
throw new NotFoundException(__('Invalid sharing group.'));
}
} else {
$sharing_group_id = 0;
$sharingGroupId = 0;
}
$object = array(
'Object' => array(
'distribution' => $distribution,
'sharing_group_id' => $sharing_group_id,
'sharing_group_id' => $sharingGroupId,
'comment' => $comment,
),
'Attribute' => array()
@ -1208,7 +1213,7 @@ class ObjectsController extends AppController
return $this->RestResponse->saveFailResponse('Objects', 'Created from Attributes', false, $error, $this->response->type());
}
} else {
$selected_attribute_ids = json_decode($selected_attribute_ids, true);
$selected_attribute_ids = $this->_jsonDecode($selected_attribute_ids);
$selected_attributes = $this->MispObject->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
@ -1228,7 +1233,7 @@ class ObjectsController extends AppController
$conformity_result = $this->MispObject->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $selected_attributes);
$skipped_attributes = 0;
foreach ($selected_attributes as $i => $attribute) {
if (in_array($attribute['Attribute']['type'], $conformity_result['invalidTypes'])) {
if (in_array($attribute['Attribute']['type'], $conformity_result['invalidTypes'], true)) {
unset($selected_attributes[$i]);
$array_position = array_search($attribute['Attribute']['id'], $selected_attribute_ids);
unset($selected_attribute_ids[$array_position]);

View File

@ -392,7 +392,7 @@ class OrganisationsController extends AppController
{
$this->layout = false;
$this->autoRender = false;
$this->set('id', $id);
$this->set('id', (int)$id);
$this->set('removable', $removable);
$this->set('extend', $extend);
$this->render('ajax/sg_org_row_empty');
@ -483,6 +483,12 @@ class OrganisationsController extends AppController
if ($logo['size'] > 0 && $logo['error'] == 0) {
$extension = pathinfo($logo['name'], PATHINFO_EXTENSION);
$filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png');
if ($extension === 'svg' && !Configure::read('Security.enable_svg_logos')) {
$this->Flash->error(__('Invalid file extension, SVG images are not allowed.'));
return false;
}
if (!empty($logo['tmp_name']) && is_uploaded_file($logo['tmp_name'])) {
return move_uploaded_file($logo['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
}

View File

@ -30,7 +30,12 @@ class PagesController extends AppController
public function display()
{
$path = func_get_args();
foreach ($path as $k => $part) {
if (strpos($part, '..') !== false || strpos($part, '/') !== false) {
unset($path[$k]);
}
}
$path = array_values($path);
$count = count($path);
if (!$count) {
$this->redirect('/');

View File

@ -39,7 +39,7 @@ class PostsController extends AppController
$event_id = 0;
$post_id = 0;
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->layout = false;
}
// we have a target type and a target id. The target id defines what type of object we want to attach this event to (is it a reply to another post,
// did someone add a post to a thread, does a thread for the event exist already, etc.

View File

@ -1,7 +1,9 @@
<?php
App::uses('AppController', 'Controller');
/**
* @property RestClientHistory $RestClientHistory
*/
class RestClientHistoryController extends AppController
{
public $components = array(
@ -37,19 +39,16 @@ class RestClientHistoryController extends AppController
}
if ($this->_isRest()) {
$list = $this->RestClientHistory->find('all', $params);
} else {
$this->paginate = array_merge($this->paginate, $params);
$list = $this->paginate();
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($list, $this->response->type());
} else {
$this->set('bookmarked', $bookmarked);
$this->set('list', $list);
$this->layout = false;
$this->autoRender = false;
$this->render('index');
}
$this->paginate = array_merge($this->paginate, $params);
$list = $this->paginate();
$this->set('bookmarked', $bookmarked);
$this->set('list', array_column($list, 'RestClientHistory'));
$this->layout = false;
$this->autoRender = false;
$this->render('index');
}
public function delete($id)

View File

@ -2,6 +2,7 @@
App::uses('AppController', 'Controller');
App::uses('Xml', 'Utility');
App::uses('AttachmentTool', 'Tools');
App::uses('JsonTool', 'Tools');
App::uses('SecurityAudit', 'Tools');
/**
@ -38,7 +39,6 @@ class ServersController extends AppController
$this->Auth->allow(['cspReport']); // cspReport must work without authentication
parent::beforeFilter();
$this->Security->unlockedActions[] = 'getApiInfo';
$this->Security->unlockedActions[] = 'cspReport';
// permit reuse of CSRF tokens on some pages.
switch ($this->request->params['action']) {
@ -322,7 +322,8 @@ class ServersController extends AppController
'json' => '[]',
'push_rules' => $defaultPushRules,
'pull_rules' => $defaultPullRules,
'self_signed' => 0
'self_signed' => 0,
'remove_missing_tags' => 0
);
foreach ($defaults as $default => $dvalue) {
if (!isset($this->request->data['Server'][$default])) {
@ -442,20 +443,7 @@ class ServersController extends AppController
$allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']);
}
$allTypes = [];
$this->loadModel('Attribute');
$this->loadModel('ObjectTemplate');
$objects = $this->ObjectTemplate->find('all', [
'recursive' => -1,
'fields' => ['uuid', 'name'],
'group' => ['uuid', 'name'],
]);
$allTypes = [
'attribute' => array_unique(Hash::extract(Hash::extract($this->Attribute->categoryDefinitions, '{s}.types'), '{n}.{n}')),
'object' => Hash::map($objects, '{n}.ObjectTemplate', function ($item) {
return ['id' => $item['uuid'], 'name' => sprintf('%s (%s)', $item['name'], $item['uuid'])];
})
];
$allTypes = $this->Server->getAllTypes();
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('organisationOptions', $organisationOptions);
@ -467,6 +455,7 @@ class ServersController extends AppController
$this->set('allTags', $this->__getTags());
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('pull_scope', 'server');
$this->render('edit');
}
}
@ -526,7 +515,7 @@ class ServersController extends AppController
}
if (!$fail) {
// say what fields are to be updated
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
$this->request->data['Server']['id'] = $id;
if (isset($this->request->data['Server']['authkey']) && "" != $this->request->data['Server']['authkey']) {
$fieldList[] = 'authkey';
@ -640,20 +629,7 @@ class ServersController extends AppController
$allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']);
}
$allTypes = [];
$this->loadModel('Attribute');
$this->loadModel('ObjectTemplate');
$objects = $this->ObjectTemplate->find('all', [
'recursive' => -1,
'fields' => ['uuid', 'name'],
'group' => ['uuid', 'name'],
]);
$allTypes = [
'attribute' => array_unique(Hash::extract(Hash::extract($this->Attribute->categoryDefinitions, '{s}.types'), '{n}.{n}')),
'object' => Hash::map($objects, '{n}.ObjectTemplate', function ($item) {
return ['id' => $item['uuid'], 'name' => sprintf('%s (%s)', $item['name'], $item['uuid'])];
})
];
$allTypes = $this->Server->getAllTypes();
$oldRemoteSetting = 0;
if (!$this->Server->data['RemoteOrg']['local']) {
@ -674,6 +650,7 @@ class ServersController extends AppController
$this->set('server', $s);
$this->set('id', $id);
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('pull_scope', 'server');
}
}
@ -907,7 +884,7 @@ class ServersController extends AppController
);
$message = sprintf(__('Push queued for background execution. Job ID: %s'), $jobId);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'push', $message, $this->response->type());
}
@ -972,7 +949,14 @@ class ServersController extends AppController
public function serverSettingsReloadSetting($setting, $id)
{
$pathToSetting = explode('.', $setting);
if (strpos($setting, 'Plugin.Enrichment') !== false || strpos($setting, 'Plugin.Import') !== false || strpos($setting, 'Plugin.Export') !== false || strpos($setting, 'Plugin.Cortex') !== false) {
if (
strpos($setting, 'Plugin.Enrichment') !== false ||
strpos($setting, 'Plugin.Import') !== false ||
strpos($setting, 'Plugin.Export') !== false ||
strpos($setting, 'Plugin.Cortex') !== false ||
strpos($setting, 'Plugin.Action') !== false ||
strpos($setting, 'Plugin.Workflow') !== false
) {
$settingObject = $this->Server->getCurrentServerSettings();
} else {
$settingObject = $this->Server->serverSettings;
@ -1020,7 +1004,13 @@ class ServersController extends AppController
$gpgErrors = array(0 => __('OK'), 1 => __('FAIL: settings not set'), 2 => __('FAIL: Failed to load GnuPG'), 3 => __('FAIL: Issues with the key/passphrase'), 4 => __('FAIL: sign failed'));
$proxyErrors = array(0 => __('OK'), 1 => __('not configured (so not tested)'), 2 => __('Getting URL via proxy failed'));
$zmqErrors = array(0 => __('OK'), 1 => __('not enabled (so not tested)'), 2 => __('Python ZeroMQ library not installed correctly.'), 3 => __('ZeroMQ script not running.'));
$sessionErrors = array(0 => __('OK'), 1 => __('High'), 2 => __('Alternative setting used'), 3 => __('Test failed'));
$sessionErrors = array(
0 => __('OK'),
1 => __('Too many expired sessions in the database, please clear the expired sessions'),
2 => __('PHP session handler is using the default file storage. This is not recommended, please use the redis or database storage'),
8 => __('Alternative setting used'),
9 => __('Test failed')
);
$moduleErrors = array(0 => __('OK'), 1 => __('System not enabled'), 2 => __('No modules found'));
$backgroundJobsErrors = array(
0 => __('OK'),
@ -1083,6 +1073,11 @@ class ServersController extends AppController
$diagnostic_errors = 0;
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
if ($tab === 'correlations') {
$this->loadModel('Correlation');
$correlation_metrics = $this->Correlation->collectMetrics();
$this->set('correlation_metrics', $correlation_metrics);
}
if ($tab === 'files') {
$files = $this->Server->grabFiles();
$this->set('files', $files);
@ -1164,6 +1159,7 @@ class ServersController extends AppController
// get the DB diagnostics
$dbDiagnostics = $this->Server->dbSpaceUsage();
$dbSchemaDiagnostics = $this->Server->dbSchemaDiagnostic();
$dbConfiguration = $this->Server->dbConfiguration();
$redisInfo = $this->Server->redisInfo();
@ -1172,10 +1168,8 @@ class ServersController extends AppController
$moduleStatus[$type] = $this->Server->moduleDiagnostics($diagnostic_errors, $type);
}
// check the size of the session table
$sessionCount = 0;
$sessionStatus = $this->Server->sessionDiagnostics($diagnostic_errors, $sessionCount);
$this->set('sessionCount', $sessionCount);
// get php session diagnostics
$sessionStatus = $this->Server->sessionDiagnostics($diagnostic_errors);
$this->loadModel('AttachmentScan');
try {
@ -1186,7 +1180,7 @@ class ServersController extends AppController
$securityAudit = (new SecurityAudit())->run($this->Server);
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'redisInfo', 'attachmentScan', 'securityAudit');
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'dbConfiguration', 'redisInfo', 'attachmentScan', 'securityAudit');
} else {
$view = [];
}
@ -1227,6 +1221,7 @@ class ServersController extends AppController
'readableFiles' => $readableFiles,
'dbDiagnostics' => $dbDiagnostics,
'dbSchemaDiagnostics' => $dbSchemaDiagnostics,
'dbConfiguration' => $dbConfiguration,
'redisInfo' => $redisInfo,
'finalSettings' => $dumpResults,
'extensions' => $extensions,
@ -1441,33 +1436,32 @@ class ServersController extends AppController
$this->render('ajax/submoduleStatus');
}
public function getSetting($setting_name)
public function getSetting($settingName)
{
$setting = $this->Server->getSettingData($setting_name);
if (!empty($setting["redacted"])) {
throw new MethodNotAllowedException(__('This setting is redacted.'));
$setting = $this->Server->getSettingData($settingName);
if (!$setting) {
throw new NotFoundException(__('Setting %s is invalid.', $settingName));
}
if (Configure::check($setting_name)) {
$setting['value'] = Configure::read($setting_name);
if (!empty($setting["redacted"])) {
throw new ForbiddenException(__('This setting is redacted.'));
}
if (Configure::check($settingName)) {
$setting['value'] = Configure::read($settingName);
}
return $this->RestResponse->viewData($setting);
}
public function serverSettingsEdit($setting_name, $id = false, $forceSave = false)
public function serverSettingsEdit($settingName, $id = false, $forceSave = false)
{
if (!isset($setting_name)) {
throw new MethodNotAllowedException();
}
if (!$this->_isRest()) {
if (!isset($id)) {
throw new MethodNotAllowedException();
}
$this->set('id', $id);
}
$setting = $this->Server->getSettingData($setting_name);
$setting = $this->Server->getSettingData($settingName);
if ($setting === false) {
throw new NotFoundException(__('Setting %s is invalid.', $setting_name));
throw new NotFoundException(__('Setting %s is invalid.', $settingName));
}
if (!empty($setting['cli_only'])) {
throw new MethodNotAllowedException(__('This setting can only be edited via the CLI.'));
@ -1488,7 +1482,10 @@ class ServersController extends AppController
$subGroup = 'general';
}
if ($this->_isRest()) {
return $this->RestResponse->viewData(array($setting['name'] => $setting['value']));
if (!empty($setting['redacted'])) {
throw new ForbiddenException(__('This setting is redacted.'));
}
return $this->RestResponse->viewData([$setting['name'] => $setting['value']]);
} else {
$this->set('subGroup', $subGroup);
$this->set('setting', $setting);
@ -1933,7 +1930,7 @@ class ServersController extends AppController
$dbVersion = $this->AdminSetting->getSetting('db_version');
$updateProgress = $this->Server->getUpdateProgress();
$updateProgress['db_version'] = $dbVersion;
$maxUpdateNumber = max(array_keys($this->Server->db_changes));
$maxUpdateNumber = max(array_keys(Server::DB_CHANGES));
$updateProgress['complete_update_remaining'] = max($maxUpdateNumber - $dbVersion, 0);
$updateProgress['update_locked'] = $this->Server->isUpdateLocked();
$updateProgress['lock_remaining_time'] = $this->Server->getLockRemainingTime();
@ -1992,280 +1989,6 @@ class ServersController extends AppController
return $this->RestResponse->viewData(array('uuid' => Configure::read('MISP.uuid')), $this->response->type());
}
public function rest()
{
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
$allValidApisFieldsContraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
if ($this->request->is('post')) {
$request = $this->request->data;
if (!empty($request['Server'])) {
$request = $this->request->data['Server'];
}
$curl = '';
$python = '';
try {
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
}
} catch (Exception $e) {
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
}
}
$header = sprintf(
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
__('YOUR_API_KEY')
);
$this->set('header', $header);
$this->set('allValidApis', $allValidApis);
// formating for optgroup
$allValidApisFormated = array();
foreach ($allValidApis as $endpoint_url => $endpoint_data) {
$allValidApisFormated[$endpoint_data['controller']][] = array('url' => $endpoint_url, 'action' => $endpoint_data['action']);
}
$this->set('allValidApisFormated', $allValidApisFormated);
$this->set('allValidApisFieldsContraint', $allValidApisFieldsContraint);
}
/**
* @param array $request
* @param string $curl
* @param string $python
* @return array|false
*/
private function __doRestQuery(array $request, &$curl = false, &$python = false)
{
App::uses('SyncTool', 'Tools');
$params = array();
$logHeaders = $request['header'];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$logHeaders = explode("\n", $request['header']);
foreach ($logHeaders as $k => $header) {
if (strpos($header, 'Authorization') !== false) {
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
}
}
$logHeaders = implode("\n", $logHeaders);
}
if (empty($request['body'])) {
$historyBody = '';
} else if (strlen($request['body']) > 65535) {
$historyBody = ''; // body is too long to save into history table
} else {
$historyBody = $request['body'];
}
$rest_history_item = array(
'org_id' => $this->Auth->user('org_id'),
'user_id' => $this->Auth->user('id'),
'headers' => $logHeaders,
'body' => $historyBody,
'url' => $request['url'],
'http_method' => $request['method'],
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
'show_result' => $request['show_result'],
'skip_ssl' => $request['skip_ssl_validation'],
'bookmark' => $request['bookmark'],
'bookmark_name' => $request['name'],
'timestamp' => time(),
);
if (!empty($request['url'])) {
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
unset($request['url']);
} else {
$url = $request['url'];
}
} else {
throw new InvalidArgumentException('URL not set.');
}
if (!empty($request['skip_ssl_validation'])) {
$params['ssl_verify_peer'] = false;
$params['ssl_verify_host'] = false;
$params['ssl_verify_peer_name'] = false;
$params['ssl_allow_self_signed'] = true;
}
$params['timeout'] = 300;
App::uses('HttpSocket', 'Network/Http');
$HttpSocket = new HttpSocket($params);
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
$request['header'] = array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => 'MISP REST Client',
);
foreach ($temp_headers as $header) {
$header = explode(':', $header);
$header[0] = trim($header[0]);
$header[1] = trim($header[1]);
$request['header'][$header[0]] = $header[1];
}
$start = microtime(true);
if (
!empty($request['method']) &&
$request['method'] === 'GET'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('get', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'POST' &&
!empty($request['body'])
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('post', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'DELETE'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('delete', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
} else {
return false;
}
$viewData = [
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
'url' => $url,
'code' => $response->code,
'headers' => $response->headers,
];
if (!empty($request['show_result'])) {
$viewData['data'] = $response->body;
} else {
if ($response->isOk()) {
$viewData['data'] = 'Success.';
} else {
$viewData['data'] = 'Something went wrong.';
}
}
$rest_history_item['outcome'] = $response->code;
$this->loadModel('RestClientHistory');
$this->RestClientHistory->create();
$this->RestClientHistory->save($rest_history_item);
$this->RestClientHistory->cleanup($this->Auth->user('id'));
return $viewData;
}
private function __generatePythonScript($request, $url)
{
$slashCounter = 0;
$baseurl = '';
$relative = '';
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
for ($i = 0; $i < strlen($url); $i++) {
//foreach ($url as $url[$i]) {
if ($url[$i] === '/') {
$slashCounter += 1;
if ($slashCounter == 3) {
continue;
}
}
if ($slashCounter < 3) {
$baseurl .= $url[$i];
} else {
$relative .= $url[$i];
}
}
$python_script =
sprintf(
'misp_url = \'%s\'
misp_key = \'%s\'
misp_verifycert = %s
relative_path = \'%s\'
body = %s
from pymisp import ExpandedPyMISP
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
misp.direct_call(relative_path, body)
',
$baseurl,
$request['header']['Authorization'],
$verifyCert,
$relative,
(empty($request['body']) ? 'None' : $request['body'])
);
return $python_script;
}
private function __generateCurlQuery($type, $request, $url)
{
if ($type === 'get') {
$curl = sprintf(
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
} else {
$curl = sprintf(
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
PHP_EOL,
json_encode(json_decode($request['body'])),
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
}
return $curl;
}
public function getApiInfo()
{
$relative_path = $this->request->data['url'];
$result = $this->RestResponse->getApiInfo($relative_path);
if ($this->_isRest()) {
if (!empty($result)) {
$result['api_info'] = $result;
}
return $this->RestResponse->viewData($result, $this->response->type());
} else {
if (empty($result)) {
return $this->RestResponse->viewData('&nbsp;', $this->response->type());
}
$this->layout = false;
$this->autoRender = false;
$this->set('api_info', $result);
$this->render('ajax/get_api_info');
}
}
public function cache($id = 'all')
{
if (Configure::read('MISP.background_jobs')) {
@ -2340,11 +2063,23 @@ misp.direct_call(relative_path, body)
if (empty($host_org)) {
throw new MethodNotAllowedException(__('Configured host org not found. Please make sure that the setting is current on the instance.'));
}
if (Configure::read('Security.advanced_authkeys')) {
$this->loadModel('AuthKey');
$authkey = $this->AuthKey->createnewkey($this->Auth->user('id'), null, __('Auto generated sync key - %s', date('Y-m-d H:i:s')));
} else {
$this->loadModel('User');
$authkey = $this->User->find('column', [
'conditions' => ['User.id' => $this->Auth->user('id')],
'recursive' => -1,
'fields' => ['User.authkey']
]);
$authkey = $authkey[0];
}
$server = array(
'Server' => array(
'url' => $baseurl,
'uuid' => Configure::read('MISP.uuid'),
'authkey' => __('YOUR_API_KEY'),
'authkey' => h($authkey),
'Organisation' => array(
'name' => $host_org['Organisation']['name'],
'uuid' => $host_org['Organisation']['uuid'],
@ -2484,13 +2219,24 @@ misp.direct_call(relative_path, body)
}
}
public function dbConfiguration()
{
$dbConfiguration = $this->Server->dbConfiguration();
if ($this->_isRest()) {
return $this->RestResponse->viewData($dbConfiguration, $this->response->type());
} else {
$this->set('dbConfiguration', $dbConfiguration);
$this->render('/Elements/healthElements/db_config_diagnostic');
}
}
public function cspReport()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('This action expects a POST request.');
}
$report = $this->Server->jsonDecode($this->request->input());
$report = JsonTool::decode($this->request->input());
if (!isset($report['csp-report'])) {
throw new RuntimeException("Invalid report");
}
@ -2500,20 +2246,13 @@ misp.direct_call(relative_path, body)
if ($remoteIp) {
$message .= ' from IP ' . $remoteIp;
}
$this->log("$message: " . json_encode($report['csp-report'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return new CakeResponse(['statusCodes' => 204]);
}
public function viewDeprecatedFunctionUse()
{
$data = $this->Deprecation->getDeprecatedAccessList($this->Server);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->layout = false;
$this->set('data', $data);
$report = JsonTool::encode($report['csp-report'], true);
if (strlen($report) > 1024 * 1024) { // limit report to 1 kB
$report = substr($report, 0, 1024 * 1024) . '...';
}
$this->log("$message: $report");
return new CakeResponse(['status' => 204]);
}
/**
@ -2570,9 +2309,6 @@ misp.direct_call(relative_path, body)
return $this->RestResponse->viewData($syncFilteringRules);
}
public function openapi() {
}
public function pruneDuplicateUUIDs()
{
if (!$this->request->is('post')) {
@ -2706,4 +2442,49 @@ misp.direct_call(relative_path, body)
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
}
public function ipUser($input = false)
{
$params = $this->IndexFilter->harvestParameters(['ip']);
if (!empty($params['ip'])) {
$input = $params['ip'];
}
$redis = $this->Server->setupRedis();
if (!is_array($input)) {
$input = [$input];
}
$users = [];
foreach ($input as $ip) {
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
continue;
}
$user_id = $redis->get('misp:ip_user:' . $ip);
if (empty($user_id)) {
continue;
}
$this->loadModel('User');
$user = $this->User->find('first', [
'recursive' => -1,
'conditions' => ['User.id' => $user_id],
'contain' => ['Organisation.name']
]);
if (empty($user)) {
throw new NotFoundException(__('User not found (perhaps it has been removed?).'));
}
$users[$ip] = [
'id' => $user['User']['id'],
'email' => $user['User']['email'],
];
}
return $this->RestResponse->viewData($users, $this->response->type());
}
/**
* @deprecated
* @return void
*/
public function rest()
{
$this->redirect(['controller' => 'api', 'action' => 'rest']);
}
}

View File

@ -254,7 +254,7 @@ class ShadowAttributesController extends AppController
{
if ($this->request->is('ajax')) {
$this->set('ajax', true);
$this->layout = 'ajax';
$this->layout = false;
} else {
$this->set('ajax', false);
}
@ -406,27 +406,14 @@ class ShadowAttributesController extends AppController
}
$this->set('event_id', $event['Event']['id']);
// combobox for types
$types = array_keys($this->ShadowAttribute->typeDefinitions);
foreach ($types as $key => $value) {
if (in_array($value, array('malware-sample', 'attachment'))) {
unset($types[$key]);
}
}
$types = $this->ShadowAttribute->Attribute->getNonAttachmentTypes();
$types = $this->_arrayToValuesIndexArray($types);
$this->set('types', $types);
// combobox for categories
$categories = array_keys($this->ShadowAttribute->Event->Attribute->categoryDefinitions);
$categories = array_keys($this->ShadowAttribute->Attribute->categoryDefinitions);
$categories = $this->_arrayToValuesIndexArray($categories);
$this->set('categories', $categories);
$fieldDesc = ['category' => [], 'type' => []];
foreach ($this->ShadowAttribute->categoryDefinitions as $key => $value) {
$fieldDesc['category'][$key] = isset($value['formdesc']) ? $value['formdesc'] : $value['desc'];
}
foreach ($this->ShadowAttribute->typeDefinitions as $key => $value) {
$fieldDesc['type'][$key] = isset($value['formdesc']) ? $value['formdesc'] : $value['desc'];
}
$this->set('fieldDesc', $fieldDesc);
$this->__common();
$this->set('categoryDefinitions', $this->ShadowAttribute->categoryDefinitions);
}
@ -531,19 +518,19 @@ class ShadowAttributesController extends AppController
}
} else {
$shadowAttribute = array(
'ShadowAttribute' => array(
'value' => $filename,
'category' => $this->request->data['ShadowAttribute']['category'],
'type' => 'attachment',
'event_id' => $this->request->data['ShadowAttribute']['event_id'],
'comment' => $this->request->data['ShadowAttribute']['comment'],
'data' => base64_encode($tmpfile->read()),
'to_ids' => 0,
'email' => $this->Auth->user('email'),
'org_id' => $this->Auth->user('org_id'),
'event_uuid' => $event['Event']['uuid'],
'event_org_id' => $event['Event']['orgc_id'],
)
'ShadowAttribute' => array(
'value' => $filename,
'category' => $this->request->data['ShadowAttribute']['category'],
'type' => 'attachment',
'event_id' => $this->request->data['ShadowAttribute']['event_id'],
'comment' => $this->request->data['ShadowAttribute']['comment'],
'data' => base64_encode($tmpfile->read()),
'to_ids' => 0,
'email' => $this->Auth->user('email'),
'org_id' => $this->Auth->user('org_id'),
'event_uuid' => $event['Event']['uuid'],
'event_org_id' => $event['Event']['orgc_id'],
)
);
$this->ShadowAttribute->create();
$r = $this->ShadowAttribute->save($shadowAttribute);
@ -593,17 +580,14 @@ class ShadowAttributesController extends AppController
$categories = $this->_arrayToValuesIndexArray($selectedCategories);
$this->set('categories', $categories);
foreach ($this->ShadowAttribute->categoryDefinitions as $key => $value) {
$info['category'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']);
}
foreach ($this->ShadowAttribute->typeDefinitions as $key => $value) {
$info['type'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']);
}
$this->set('info', $info);
$this->__common();
$this->set('attrDescriptions', $this->ShadowAttribute->fieldDescriptions);
$this->set('typeDefinitions', $this->ShadowAttribute->typeDefinitions);
$this->set('categoryDefinitions', $this->ShadowAttribute->categoryDefinitions);
$this->set('isMalwareSampleCategory', $isMalwareSampleCategory);
$this->set('mayModify', $this->__canModifyEvent($event));
$this->set('event', $event);
$this->set('title_for_layout', __('Propose attachment'));
}
// Propose an edit to an attribute
@ -623,7 +607,7 @@ class ShadowAttributesController extends AppController
$existingAttribute = $existingAttribute[0];
// Check if the attribute is an attachment, if yes, block the type and the value fields from being edited.
if ('attachment' == $existingAttribute['Attribute']['type'] || 'malware-sample' == $existingAttribute['Attribute']['type']) {
if ($this->ShadowAttribute->Attribute->typeIsAttachment($existingAttribute['Attribute']['type'])) {
$this->set('attachment', true);
$attachment = true;
} else {
@ -718,12 +702,7 @@ class ShadowAttributesController extends AppController
}
// combobox for types
$types = array_keys($this->ShadowAttribute->typeDefinitions);
foreach ($types as $key => $value) {
if (in_array($value, array('malware-sample', 'attachment'))) {
unset($types[$key]);
}
}
$types = $this->ShadowAttribute->Attribute->getNonAttachmentTypes();
if ($existingAttribute['Attribute']['object_id']) {
$this->set('objectAttribute', true);
} else {
@ -732,15 +711,10 @@ class ShadowAttributesController extends AppController
$types = $this->_arrayToValuesIndexArray($types);
$this->set('types', $types);
// combobox for categories
$categories = $this->_arrayToValuesIndexArray(array_keys($this->ShadowAttribute->Event->Attribute->categoryDefinitions));
$categories = $this->_arrayToValuesIndexArray(array_keys($this->ShadowAttribute->Attribute->categoryDefinitions));
$categories = $this->_arrayToValuesIndexArray($categories);
foreach ($this->ShadowAttribute->Event->Attribute->categoryDefinitions as $key => $value) {
$info['category'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']);
}
foreach ($this->ShadowAttribute->Event->Attribute->typeDefinitions as $key => $value) {
$info['type'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']);
}
$categoryDefinitions = $this->ShadowAttribute->Event->Attribute->categoryDefinitions;
$categoryDefinitions = $this->ShadowAttribute->Attribute->categoryDefinitions;
if ($existingAttribute['Attribute']['object_id']) {
foreach ($categoryDefinitions as $k => $v) {
if (!in_array($existingAttribute['Attribute']['type'], $v['types'])) {
@ -754,10 +728,22 @@ class ShadowAttributesController extends AppController
}
}
$this->set('categories', $categories);
$this->set('info', $info);
$this->__common();
$this->set('attrDescriptions', $this->ShadowAttribute->fieldDescriptions);
$this->set('typeDefinitions', $this->ShadowAttribute->typeDefinitions);
$this->set('categoryDefinitions', $this->ShadowAttribute->Event->Attribute->categoryDefinitions);
$this->set('categoryDefinitions', $this->ShadowAttribute->Attribute->categoryDefinitions);
}
private function __common()
{
$fieldDesc = ['category' => [], 'type' => []];
foreach ($this->ShadowAttribute->categoryDefinitions as $key => $value) {
$fieldDesc['category'][$key] = isset($value['formdesc']) ? $value['formdesc'] : $value['desc'];
}
foreach ($this->ShadowAttribute->typeDefinitions as $key => $value) {
$fieldDesc['type'][$key] = isset($value['formdesc']) ? $value['formdesc'] : $value['desc'];
}
$this->set('fieldDesc', $fieldDesc);
}
public function delete($id)

View File

@ -0,0 +1,196 @@
<?php
App::uses('AppController', 'Controller');
class SharingGroupBlueprintsController extends AppController
{
public $components = array('Session', 'RequestHandler');
public function beforeFilter()
{
parent::beforeFilter();
}
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999
);
public function index()
{
$params = [
'filters' => ['name', 'uuid'],
'quickFilters' => ['name']
];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'indexMG'));
}
public function add()
{
$currentUser = $this->Auth->user();
$params = [
'beforeSave' => function($data) use ($currentUser) {
$data['SharingGroupBlueprint']['uuid'] = CakeText::uuid();
$data['SharingGroupBlueprint']['user_id'] = $currentUser['id'];
$data['SharingGroupBlueprint']['org_id'] = $currentUser['org_id'];
return $data;
}
];
$this->CRUD->add($params);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'addMG'));
}
public function edit($id)
{
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'editMG'));
$this->set('id', $id);
$params = [
'fields' => ['rules', 'name']
];
$this->CRUD->edit($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->render('add');
}
public function delete($id)
{
$this->CRUD->delete($id);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function view($id)
{
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'view_cerebrate']);
$this->CRUD->view($id, ['contain' => ['Organisation.name', 'Organisation.uuid', 'Organisation.id', 'SharingGroup.id', 'SharingGroup.name']]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('id', $id);
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'viewMG'));
}
public function viewOrgs($id)
{
$conditions = ['SharingGroupBlueprint.id' => $id];
if (!$this->_isSiteAdmin()) {
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
}
$sharingGroupBlueprint = $this->SharingGroupBlueprint->find('first', ['conditions' => $conditions]);
if (empty($sharingGroupBlueprint)) {
throw new NotFoundException(__('Invalid Sharing Group Blueprint'));
}
// we create a fake user to restrict the visible sharing groups to the creator of the SharingGroupBlueprint, in case an admin wants to update it
$fake_user = [
'Role' => [
'perm_site_admin' => false
],
'org_id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id'],
'id' => 1
];
$temp = $this->SharingGroupBlueprint->evaluateSharingGroupBlueprint($sharingGroupBlueprint, $fake_user);
$orgs = $this->SharingGroupBlueprint->SharingGroup->Organisation->find('all', [
'recursive' => -1,
'fields' => ['id', 'uuid', 'name', 'sector', 'type', 'nationality'],
'conditions' => ['id' => $temp['orgs']]
]);
$this->set('data', $orgs);
$this->set('menuData', array('menuList' => 'SharingGroupBlueprints', 'menuItem' => 'viewOrgs'));
}
public function execute($id = false)
{
$conditions = [];
if (!empty($id)) {
$conditions['SharingGroupBlueprint.id'] = $id;
}
if (!$this->Auth->user('Role')['perm_admin']) {
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
}
$sharingGroupBlueprints = $this->SharingGroupBlueprint->find('all', ['conditions' => $conditions, 'recursive' => 0]);
if (empty($sharingGroupBlueprints)) {
throw new NotFoundException(__('No valid blueprints found.'));
}
if ($this->request->is('post')) {
$stats = $this->SharingGroupBlueprint->execute($sharingGroupBlueprints);
$message = __(
'Done, %s sharing group blueprint(s) matched. Sharing group changes: Created: %s. Updated: %s. Failed to create: %s.',
count($sharingGroupBlueprints),
$stats['created'],
$stats['changed'],
$stats['failed']
);
if ($this->IndexFilter->isRest()) {
if ($stats['changed'] || $stats['created']) {
return $this->RestResponse->saveSuccessResponse('sharingGroupBlueprints', 'execute', $id, false, $message);
} else {
return $this->RestResponse->saveFailResponse('sharingGroupBlueprints', 'execute', $id, $message, $this->response->type());
}
} else {
$status = 'success';
if ($stats['failed']) {
$status = 'error';
if ($stats['created'] || $stats['changed']) {
$status = 'info';
}
}
$this->Flash->{$status}($message);
$this->redirect($this->referer());
}
} else {
$this->set('id', empty($id) ? $id : 'all');
$this->set('title', __('Execute Sharing Group Blueprint'));
$this->set('question', __('Are you sure you want to (re)create a sharing group based on the Sharing Group Blueprint?'));
$this->set('actionName', __('Execute'));
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}
public function detach($id)
{
$conditions = [];
if (empty($id)) {
throw new MethodNotAllowedException(__('No ID specified.'));
}
$conditions['SharingGroupBlueprint.id'] = $id;
if (!$this->Auth->user('Role')['perm_admin']) {
$conditions['SharingGroupBlueprint.org_id'] = $this->Auth->user('org_id');
}
$sharingGroupBlueprint = $this->SharingGroupBlueprint->find('first', ['conditions' => $conditions, 'recursive' => -1]);
if (empty($sharingGroupBlueprint)) {
throw new NotFoundException(__('Invalid Sharing Group Blueprint'));
}
if ($this->request->is('post')) {
$sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'] = 0;
$result = $this->SharingGroupBlueprint->save($sharingGroupBlueprint);
$message = $result ? __('Sharing group detached.') : __('Could not detach sharing group.');
if ($this->IndexFilter->isRest()) {
if ($result) {
return $this->RestResponse->saveSuccessResponse('sharingGroupBlueprints', 'detach', $id, false, $message);
} else {
return $this->RestResponse->saveFailResponse('sharingGroupBlueprints', 'detach', $id, $message, $this->response->type());
}
} else {
$this->Flash->{$result ? 'success' : 'error'}($message);
$this->redirect($this->referer());
}
} else {
$this->set('id', $id);
$this->set('title', __('Detach Sharing Group Blueprint'));
$this->set('question', __('Are you sure you want to detach the associated sharing group from this Sharing Group Blueprint? This action is irreversible.'));
$this->set('actionName', __('Detach'));
$this->layout = false;
$this->render('/genericTemplates/confirm');
}
}
}

View File

@ -41,9 +41,8 @@ class SharingGroupsController extends AppController
public function add()
{
if (!$this->userRole['perm_sharing_group']) {
throw new MethodNotAllowedException('You don\'t have the required privileges to do that.');
}
$canModifyUuid = $this->Auth->user()['Role']['perm_site_admin'];
if ($this->request->is('post')) {
if ($this->_isRest()) {
if (isset($this->request->data['SharingGroup'])) {
@ -79,6 +78,9 @@ class SharingGroupsController extends AppController
}
}
$this->SharingGroup->create();
if (!$canModifyUuid) {
unset($sg['uuid']);
}
$sg['active'] = $sg['active'] ? 1: 0;
$sg['roaming'] = $sg['roaming'] ? 1: 0;
$sg['organisation_uuid'] = $this->Auth->user('Organisation')['uuid'];
@ -124,23 +126,15 @@ class SharingGroupsController extends AppController
} elseif ($this->_isRest()) {
return $this->RestResponse->describe('SharingGroup', 'add', false, $this->response->type());
}
$orgs = $this->SharingGroup->Organisation->find('all', array(
'conditions' => array('local' => 1),
'recursive' => -1,
'fields' => array('id', 'name', 'uuid')
));
$this->set('orgs', $orgs);
$this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'));
// We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit
$this->set('user', $this->Auth->user());
$this->set('canModifyUuid', $canModifyUuid);
}
public function edit($id = false)
{
if (!$this->userRole['perm_sharing_group']) {
throw new MethodNotAllowedException('You don\'t have the required privileges to do that.');
}
if (empty($id)) {
throw new NotFoundException('Invalid sharing group.');
}
@ -163,6 +157,10 @@ class SharingGroupsController extends AppController
),
),
));
if (empty($sharingGroup)) {
throw new NotFoundException('Invalid sharing group.');
}
if (!$this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sharingGroup['SharingGroup']['id'])) {
throw new MethodNotAllowedException('Action not allowed.');
}
@ -262,7 +260,7 @@ class SharingGroupsController extends AppController
public function index($passive = false)
{
$passive = $passive === 'true';
$authorizedSgIds = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
$authorizedSgIds = $this->SharingGroup->authorizedIds($this->Auth->user());
$this->paginate['conditions'][] = array('SharingGroup.id' => $authorizedSgIds);
$this->paginate['conditions'][] = array('SharingGroup.active' => $passive === true ? 0 : 1);

View File

@ -315,7 +315,7 @@ class SightingsController extends AppController
}
$this->set('csv', $statistics['csv']['all']);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/view_sightings');
}

View File

@ -13,29 +13,29 @@ class TagCollectionsController extends AppController
);
public $paginate = array(
'limit' => 60,
'order' => array(
'TagCollection.name' => 'ASC'
'limit' => 60,
'order' => array(
'TagCollection.name' => 'ASC'
),
'recursive' => -1,
'contain' => array(
'TagCollectionTag' => array(
'Tag'
),
'recursive' => -1,
'contain' => array(
'TagCollectionTag' => array(
'Tag'
),
'Organisation' => array(
'fields' => array(
'Organisation.id',
'Organisation.name',
'Organisation.uuid'
)
),
'User' => array(
'fields' => array(
'User.email',
'User.id'
)
'Organisation' => array(
'fields' => array(
'Organisation.id',
'Organisation.name',
'Organisation.uuid'
)
),
'User' => array(
'fields' => array(
'User.email',
'User.id'
)
)
)
);
public function add()
@ -387,7 +387,7 @@ class TagCollectionsController extends AppController
$this->set('tag_id', $tag_id);
$this->set('model', 'tag_collection');
$this->set('model_name', $tagCollection['TagCollection']['name']);
$this->layout = 'ajax';
$this->layout = false;
$this->render('/Attributes/ajax/tagRemoveConfirmation');
} else {
$rearrangeRules = array(
@ -446,7 +446,6 @@ class TagCollectionsController extends AppController
public function index()
{
//$this->Auth->user('Role')['perm_site_admin']);
$conditions = array();
if (!$this->_isSiteAdmin()) {
$conditions = array(
@ -512,9 +511,9 @@ class TagCollectionsController extends AppController
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($list, $this->response->type());
} else {
$this->set('list', $list);
}
$this->set('list', $list);
$this->set('title_for_layout', __('Tag Collections'));
}
public function getRow($id)

View File

@ -349,9 +349,10 @@ class TagsController extends AppController
public function showEventTag($id)
{
$user = $this->_closeSession();
$this->loadModel('Taxonomy');
$event = $this->Tag->EventTag->Event->fetchSimpleEvent($this->Auth->user(), $id, [
$event = $this->Tag->EventTag->Event->fetchSimpleEvent($user, $id, [
'fields' => ['Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.user_id'],
'contain' => [
'EventTag' => array(
@ -364,24 +365,26 @@ class TagsController extends AppController
throw new NotFoundException(__('Invalid event.'));
}
// Remove galaxy tags
$event = $this->Tag->EventTag->Event->massageTags($this->Auth->user(), $event, 'Event', false, true);
$event = $this->Tag->removeGalaxyClusterTags($user, $event);
$this->set('tags', $event['EventTag']);
$this->set('missingTaxonomies', $this->Tag->EventTag->Event->missingTaxonomies($event));
$tagConflicts = $this->Taxonomy->checkIfTagInconsistencies($event['EventTag']);
$this->set('tagConflicts', $tagConflicts);
$this->set('event', $event);
$this->layout = 'ajax';
$this->set('mayModify', $this->__canModifyEvent($event, $user));
$this->layout = false;
$this->render('/Events/ajax/ajaxTags');
}
public function showAttributeTag($id)
{
$user = $this->_closeSession();
$this->helpers[] = 'TextColour';
$this->loadModel('Attribute');
$this->loadModel('Taxonomy');
$attributes = $this->Attribute->fetchAttributes($this->Auth->user(), [
$attributes = $this->Attribute->fetchAttributes($user, [
'conditions' => ['Attribute.id' => $id],
'includeAllTags' => true,
'flatten' => true,
@ -394,7 +397,7 @@ class TagsController extends AppController
}
$attribute = $attributes[0];
// Remove galaxy tags
$attribute = $this->Tag->EventTag->Event->massageTags($this->Auth->user(), $attribute, 'Attribute', false, true);
$attribute = $this->Tag->removeGalaxyClusterTags($user, $attribute, 'Attribute');
$attributeTags = $attribute['AttributeTag'];
$this->set('event', ['Event' => $attribute['Event']]);
@ -402,7 +405,8 @@ class TagsController extends AppController
$this->set('attributeId', $id);
$tagConflicts = $this->Taxonomy->checkIfTagInconsistencies($attributeTags);
$this->set('tagConflicts', $tagConflicts);
$this->layout = 'ajax';
$this->set('mayModify', $this->__canModifyEvent($attribute, $user));
$this->layout = false;
$this->render('/Attributes/ajax/ajaxAttributeTags');
}
@ -435,7 +439,7 @@ class TagsController extends AppController
'conditions' => array('Event.id' => $id)
));
$this->set('event', $event);
$this->layout = 'ajax';
$this->layout = false;
$this->render('/Events/ajax/ajaxTags');
}
@ -456,13 +460,11 @@ class TagsController extends AppController
public function selectTaxonomy($id, $scope = 'event')
{
if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) {
throw new NotFoundException('You don\'t have permission to do that.');
}
$user = $this->_closeSession();
$localFlag = !empty($this->params['named']['local']) ? '/local:1' : '';
$items = array();
$favourites = $this->Tag->FavouriteTag->find('count', array('conditions' => array('FavouriteTag.user_id' => $this->Auth->user('id'))));
if ($favourites) {
$hasFavourites = $this->Tag->FavouriteTag->hasAny(array('FavouriteTag.user_id' => $user['id']));
if ($hasFavourites) {
$items[] = array(
'name' => __('Favourite Tags'),
'value' => $this->baseurl . "/tags/selectTag/" . h($id) . "/favourites/" . h($scope) . $localFlag
@ -484,11 +486,11 @@ class TagsController extends AppController
);
$this->loadModel('Taxonomy');
$options = $this->Taxonomy->find('list', array('conditions' => array('enabled' => true), 'fields' => array('namespace'), 'order' => array('Taxonomy.namespace ASC')));
foreach ($options as $k => $option) {
$taxonomies = $this->Taxonomy->find('list', array('conditions' => array('enabled' => true), 'fields' => array('namespace'), 'order' => array('Taxonomy.namespace ASC')));
foreach ($taxonomies as $taxonomyId => $name) {
$items[] = array(
'name' => __('Taxonomy Library') . ":" . h($option),
'value' => $this->baseurl . "/tags/selectTag/" . h($id) . "/" . h($k) . "/" . h($scope . $localFlag)
'name' => __('Taxonomy Library') . ":" . h($name),
'value' => $this->baseurl . "/tags/selectTag/" . h($id) . "/" . h($taxonomyId) . "/" . h($scope) . $localFlag
);
}
$this->set('items', $items);
@ -502,6 +504,7 @@ class TagsController extends AppController
public function selectTag($id, $taxonomy_id, $scope = 'event', $filterData = '')
{
$user = $this->_closeSession();
$this->loadModel('Taxonomy');
$expanded = array();
$this->set('taxonomy_id', $taxonomy_id);
@ -527,87 +530,81 @@ class TagsController extends AppController
$expanded[$tagCollection['TagCollection']['id']] .= sprintf(' (%s)', $tagList);
}
}
} elseif ($taxonomy_id === '0') { // custom tags
$temp = $this->Taxonomy->getAllTaxonomyTags(true, $user, true, true, $local_tag);
$tags = array();
foreach ($temp as $tag) {
$tags[$tag['Tag']['id']] = $tag['Tag'];
}
unset($temp);
$expanded = $tags;
} elseif ($taxonomy_id === 'favourites') {
$tags = array();
$conditions = array(
'FavouriteTag.user_id' => $user['id'],
'Tag.org_id' => array(0, $user['org_id']),
'Tag.user_id' => array(0, $user['id']),
'Tag.hide_tag' => 0,
);
if (!$local_tag) {
$conditions['Tag.local_only'] = 0;
}
$favTags = $this->Tag->FavouriteTag->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'contain' => array('Tag'),
'order' => array('Tag.name asc')
));
foreach ($favTags as $favTag) {
$tags[$favTag['FavouriteTag']['tag_id']] = $favTag['Tag'];
$expanded = $tags;
}
} elseif ($taxonomy_id === 'all') { // all tags
$conditions = [
'Tag.is_galaxy' => 0,
'Tag.hide_tag' => 0,
];
if (!$this->_isSiteAdmin()) {
$conditions['Tag.org_id'] = array(0, $user['org_id']);
$conditions['Tag.user_id'] = array(0, $user['id']);
}
if (!$local_tag) {
$conditions['Tag.local_only'] = 0;
}
$tags = $this->Tag->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'order' => array('name asc'),
'fields' => array('Tag.id', 'Tag.name', 'Tag.colour')
));
$tags = array_column(array_column($tags, 'Tag'), null, "id");
$expanded = $tags;
} else {
if ($taxonomy_id === '0') {
$temp = $this->Taxonomy->getAllTaxonomyTags(true, $this->Auth->user(), true, true, $local_tag);
$tags = array();
foreach ($temp as $tag) {
$tags[$tag['Tag']['id']] = $tag['Tag'];
}
unset($temp);
$expanded = $tags;
} elseif ($taxonomy_id === 'favourites') {
$tags = array();
$conditions = array(
'FavouriteTag.user_id' => $this->Auth->user('id'),
'Tag.org_id' => array(0, $this->Auth->user('org_id')),
'Tag.user_id' => array(0, $this->Auth->user('id')),
'Tag.hide_tag' => 0,
);
if (!$local_tag) {
$conditions['Tag.local_only'] = 0;
}
$favTags = $this->Tag->FavouriteTag->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'contain' => array('Tag'),
'order' => array('Tag.name asc')
));
foreach ($favTags as $favTag) {
$tags[$favTag['FavouriteTag']['tag_id']] = $favTag['Tag'];
$expanded = $tags;
}
} elseif ($taxonomy_id === 'all') {
$conditions = [
'Tag.name NOT LIKE' => 'misp-galaxy:%',
'Tag.hide_tag' => 0,
];
if (!$this->_isSiteAdmin()) {
$conditions['Tag.org_id'] = array(0, $this->Auth->user('org_id'));
$conditions['Tag.user_id'] = array(0, $this->Auth->user('id'));
}
if (!$local_tag) {
$conditions['Tag.local_only'] = 0;
}
$allTags = $this->Tag->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'order' => array('name asc'),
'fields' => array('Tag.id', 'Tag.name', 'Tag.colour')
));
$tags = array();
foreach ($allTags as $tag) {
$tags[$tag['Tag']['id']] = $tag['Tag'];
}
unset($allTags);
$expanded = $tags;
} else {
$taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id);
$tags = array();
if (!empty($taxonomies['entries'])) {
$isSiteAdmin = $this->_isSiteAdmin();
foreach ($taxonomies['entries'] as $entry) {
if (!empty($entry['existing_tag']['Tag'])) {
$tag = $entry['existing_tag']['Tag'];
if ($tag['hide_tag']) {
continue; // do not include hidden tags
}
if ($tag['local_only'] && !$local_tag) {
continue; // we skip the local tags for global entries
}
if (!$isSiteAdmin) {
// Skip all tags that this user cannot use for tagging, determined by the org restriction on tags
if ($tag['org_id'] != '0' && $tag['org_id'] != $this->Auth->user('org_id')) {
continue;
}
if ($tag['user_id'] != '0' && $tag['user_id'] != $this->Auth->user('id')) {
continue;
}
}
$tags[$tag['id']] = $tag;
$expanded[$tag['id']] = $entry['expanded'];
$taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id);
$tags = array();
if (!empty($taxonomies['entries'])) {
$isSiteAdmin = $this->_isSiteAdmin();
foreach ($taxonomies['entries'] as $entry) {
if (!empty($entry['existing_tag']['Tag'])) {
$tag = $entry['existing_tag']['Tag'];
if ($tag['hide_tag']) {
continue; // do not include hidden tags
}
if ($tag['local_only'] && !$local_tag) {
continue; // we skip the local tags for global entries
}
if (!$isSiteAdmin) {
// Skip all tags that this user cannot use for tagging, determined by the org restriction on tags
if ($tag['org_id'] != '0' && $tag['org_id'] != $user['org_id']) {
continue;
}
if ($tag['user_id'] != '0' && $tag['user_id'] != $user['id']) {
continue;
}
}
$tags[$tag['id']] = $tag;
$expanded[$tag['id']] = $entry['expanded'];
}
}
}
@ -626,7 +623,7 @@ class TagsController extends AppController
$items = array();
foreach ($tags as $k => $tag) {
$tagName = $tag['name'];
$choice_id = $k;
$choice_id = (int)$k;
if ($taxonomy_id === 'collections') {
$choice_id = 'collection_' . $choice_id;
}
@ -664,7 +661,7 @@ class TagsController extends AppController
),
));
$this->set('local', !empty($this->params['named']['local']));
$this->render('ajax/select_tag');
$this->render('/Elements/generic_picker');
}
public function tagStatistics($percentage = false, $keysort = false)
@ -1003,6 +1000,7 @@ class TagsController extends AppController
public function search($tag = false, $strictTagNameOnly = false, $searchIfTagExists = true)
{
$user = $this->_closeSession();
if (isset($this->request->data['Tag'])) {
$this->request->data = $this->request->data['Tag'];
}
@ -1022,7 +1020,7 @@ class TagsController extends AppController
$tag[$k] = strtolower($t);
$conditionsCluster['OR'][] = array('LOWER(GalaxyCluster.value)' => $tag[$k]);
}
foreach ($tag as $k => $t) {
foreach ($tag as $t) {
$conditionsCluster['OR'][] = array('AND' => array('GalaxyElement.key' => 'synonyms', 'LOWER(GalaxyElement.value) LIKE' => $t));
}
$elements = $this->GalaxyCluster->GalaxyElement->find('all', array(
@ -1033,7 +1031,7 @@ class TagsController extends AppController
foreach ($elements as $element) {
$tag[] = strtolower($element['GalaxyCluster']['tag_name']);
}
foreach ($tag as $k => $t) {
foreach ($tag as $t) {
$conditions['OR'][] = array('LOWER(Tag.name) LIKE' => $t);
}
} else {
@ -1067,7 +1065,7 @@ class TagsController extends AppController
$tags[$k]['Taxonomy'] = $taxonomy['Taxonomy'];
$tags[$k]['TaxonomyPredicate'] = $taxonomy['TaxonomyPredicate'][0];
}
$cluster = $this->GalaxyCluster->getCluster($t['Tag']['name'], $this->Auth->user());
$cluster = $this->GalaxyCluster->getCluster($t['Tag']['name'], $user);
if (!empty($cluster)) {
$dataFound = true;
$tags[$k]['GalaxyCluster'] = $cluster['GalaxyCluster'];

View File

@ -183,27 +183,17 @@ class TaxonomiesController extends AppController
public function enable($id)
{
if (!$this->_isSiteAdmin() || !$this->request->is('Post')) {
throw new MethodNotAllowedException(__('You don\'t have permission to do that.'));
}
$this->request->allowMethod(['post']);
$taxonomy = $this->Taxonomy->find('first', array(
'recursive' => -1,
'conditions' => array('Taxonomy.id' => $id),
));
$taxonomy['Taxonomy']['enabled'] = true;
$this->Taxonomy->save($taxonomy);
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Taxonomy',
'model_id' => $id,
'email' => $this->Auth->user('email'),
'action' => 'enable',
'user_id' => $this->Auth->user('id'),
'title' => 'Taxonomy enabled',
'change' => $taxonomy['Taxonomy']['namespace'] . ' - enabled',
));
$this->__log('enable', $id, 'Taxonomy enabled', $taxonomy['Taxonomy']['namespace'] . ' - enabled');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'enable', $id, $this->response->type());
} else {
@ -214,28 +204,18 @@ class TaxonomiesController extends AppController
public function disable($id)
{
if (!$this->_isSiteAdmin() || !$this->request->is('Post')) {
throw new MethodNotAllowedException(__('You don\'t have permission to do that.'));
}
$this->request->allowMethod(['post']);
$taxonomy = $this->Taxonomy->find('first', array(
'recursive' => -1,
'conditions' => array('Taxonomy.id' => $id),
'recursive' => -1,
'conditions' => array('Taxonomy.id' => $id),
));
$this->Taxonomy->disableTags($id);
$taxonomy['Taxonomy']['enabled'] = 0;
$this->Taxonomy->save($taxonomy);
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Taxonomy',
'model_id' => $id,
'email' => $this->Auth->user('email'),
'action' => 'disable',
'user_id' => $this->Auth->user('id'),
'title' => 'Taxonomy disabled',
'change' => $taxonomy['Taxonomy']['namespace'] . ' - disabled',
));
$this->__log('disable', $id, 'Taxonomy disabled', $taxonomy['Taxonomy']['namespace'] . ' - disabled');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'disable', $id, $this->response->type());
} else {
@ -246,9 +226,8 @@ class TaxonomiesController extends AppController
public function import()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('This endpoint requires a POST request.');
}
$this->request->allowMethod(['post']);
try {
$id = $this->Taxonomy->import($this->request->data);
return $this->view($id);
@ -260,7 +239,6 @@ class TaxonomiesController extends AppController
public function update()
{
$result = $this->Taxonomy->update();
$this->Log = ClassRegistry::init('Log');
$fails = 0;
$successes = 0;
if (!empty($result)) {
@ -271,50 +249,19 @@ class TaxonomiesController extends AppController
} else {
$change = $success['namespace'] . ' v' . $success['new'] . ' installed';
}
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Taxonomy',
'model_id' => $id,
'email' => $this->Auth->user('email'),
'action' => 'update',
'user_id' => $this->Auth->user('id'),
'title' => 'Taxonomy updated',
'change' => $change,
));
$this->__log('update', $id, 'Taxonomy updated', $change);
$successes++;
}
}
if (isset($result['fails'])) {
foreach ($result['fails'] as $id => $fail) {
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Taxonomy',
'model_id' => $id,
'email' => $this->Auth->user('email'),
'action' => 'update',
'user_id' => $this->Auth->user('id'),
'title' => 'Taxonomy failed to update',
'change' => $fail['namespace'] . ' could not be installed/updated. Error: ' . $fail['fail'],
));
$this->__log('update', $id, 'Taxonomy failed to update', $fail['namespace'] . ' could not be installed/updated. Error: ' . $fail['fail']);
$fails++;
}
}
} else {
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Taxonomy',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'update',
'user_id' => $this->Auth->user('id'),
'title' => 'Taxonomy update (nothing to update)',
'change' => 'Executed an update of the taxonomy library, but there was nothing to update.',
));
$this->__log('update', 0, 'Taxonomy update (nothing to update)', 'Executed an update of the taxonomy library, but there was nothing to update.');
}
$message = '';
if ($successes == 0 && $fails == 0) {
$flashType = 'info';
$message = __('All taxonomy libraries are up to date already.');
@ -323,9 +270,9 @@ class TaxonomiesController extends AppController
$message = __('Could not update any of the taxonomy libraries');
} else {
$flashType = 'success';
$message = __('Successfully updated ') . $successes . __(' taxonomy libraries.');
$message = __('Successfully updated %s taxonomy libraries.', $successes);
if ($fails != 0) {
$message .= __(' However, could not update ') . $fails . __(' taxonomy libraries.');
$message .= __(' However, could not update %s taxonomy libraries.', $fails);
}
}
if ($this->_isRest()) {
@ -338,9 +285,6 @@ class TaxonomiesController extends AppController
public function addTag($taxonomy_id = false)
{
if ((!$this->_isSiteAdmin() && !$this->userRole['perm_tagger'])) {
throw new NotFoundException(__('You don\'t have permission to do that.'));
}
if ($this->request->is('get')) {
if (empty($taxonomy_id) && !empty($this->request->params['named']['taxonomy_id'])) {
$taxonomy_id = $this->request->params['named']['taxonomy_id'];
@ -391,9 +335,8 @@ class TaxonomiesController extends AppController
public function hideTag($taxonomy_id = false)
{
if ((!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) || !$this->request->is('post')) {
throw new NotFoundException(__('You don\'t have permission to do that.'));
}
$this->request->allowMethod(['post']);
if ($taxonomy_id) {
$result = $this->Taxonomy->hideTags($taxonomy_id);
} else {
@ -421,9 +364,8 @@ class TaxonomiesController extends AppController
public function unhideTag($taxonomy_id = false)
{
if ((!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) || !$this->request->is('post')) {
throw new NotFoundException(__('You don\'t have permission to do that.'));
}
$this->request->allowMethod(['post']);
if ($taxonomy_id) {
$result = $this->Taxonomy->unhideTags($taxonomy_id);
} else {
@ -451,9 +393,6 @@ class TaxonomiesController extends AppController
public function disableTag($taxonomy_id = false)
{
if ((!$this->_isSiteAdmin() && !$this->userRole['perm_tagger'])) {
throw new NotFoundException(__('You don\'t have permission to do that.'));
}
if ($this->request->is('get')) {
if (empty($taxonomy_id) && !empty($this->request->params['named']['taxonomy_id'])) {
$taxonomy_id = $this->request->params['named']['taxonomy_id'];
@ -555,10 +494,25 @@ class TaxonomiesController extends AppController
$this->set('required', !$taxonomy['Taxonomy']['required']);
$this->set('id', $id);
$this->autoRender = false;
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/toggle_required');
}
/**
* @param string $action
* @param int $modelId
* @param string $title
* @param string $change
* @return void
* @throws Exception
*/
private function __log($action, $modelId, $title, $change)
{
/** @var Log $log */
$log = ClassRegistry::init('Log');
$log->createLogEntry($this->Auth->user(), $action, 'Taxonomy', $modelId, $title, $change);
}
/**
* Attach tag counts.
* @param array $taxonomies
@ -640,4 +594,14 @@ class TaxonomiesController extends AppController
return $taxonomyIds;
}
public function normalizeCustomTagsToTaxonomyFormat()
{
$this->request->allowMethod(['post', 'put']);
$conversionResult = $this->Taxonomy->normalizeCustomTagsToTaxonomyFormat();
$this->Flash->success(__('%s tags successfully converted. %s row updated.', $conversionResult['tag_converted'], $conversionResult['row_updated']));
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
}
}

View File

@ -38,7 +38,7 @@ class TemplateElementsController extends AppController
$this->loadModel('Attribute');
$this->set('validTypeGroups', $this->Attribute->validTypeGroups);
$this->set('id', $id);
$this->layout = 'ajaxTemplate';
$this->layout = false;
$this->set('elements', $templateElements);
$mayModify = false;
if ($this->_isSiteAdmin() || $template['Template']['org'] == $this->Auth->user('Organisation')['name']) {
@ -57,7 +57,7 @@ class TemplateElementsController extends AppController
throw new MethodNotAllowedException('This action is for ajax requests only.');
}
$this->set('id', $id);
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/template_element_add_choices');
}
@ -116,7 +116,7 @@ class TemplateElementsController extends AppController
$this->set('categoryArray', $categoryArray);
$this->set('categories', $categories);
}
$this->layout = 'ajaxTemplate';
$this->layout = false;
$this->render('ajax/template_element_add_' . $type);
} elseif ($this->request->is('post')) {
$pos = $this->TemplateElement->lastPosition($id);
@ -210,7 +210,7 @@ class TemplateElementsController extends AppController
$this->set('categoryArray', $categoryArray);
$this->set('categories', $categories);
}
$this->layout = 'ajaxTemplate';
$this->layout = false;
$this->render('ajax/template_element_edit_' . $type);
} elseif ($this->request->is('post') || $this->request->is('put')) {
$this->request->data[$ModelType]['id'] = $templateElement[$ModelType][0]['id'];

View File

@ -139,7 +139,7 @@ class ThreadsController extends AppController
}
}
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->layout = false;
$this->render('/Elements/eventdiscussion');
}
}
@ -148,7 +148,7 @@ class ThreadsController extends AppController
{
$this->loadModel('Posts');
$this->loadModel('SharingGroup');
$sgids = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
$sgids = $this->SharingGroup->authorizedIds($this->Auth->user());
$conditions = null;
if (!$this->_isSiteAdmin()) {
$conditions['AND']['OR'] = array(

View File

@ -97,6 +97,11 @@ class UserSettingsController extends AppController
);
}
}
// Do not show internal settings
if (!$this->_isSiteAdmin()) {
$conditions['AND'][] = ['NOT' => ['UserSetting.setting' => $this->UserSetting->getInternalSettingNames()]];
}
if ($this->_isRest()) {
$params = array(
'conditions' => $conditions
@ -127,9 +132,12 @@ class UserSettingsController extends AppController
public function view($id)
{
if (!$this->_isRest()) {
throw new BadRequestException("This endpoint is accessible just by REST requests.");
}
// check if the ID is valid and whether a user setting with the given ID exists
if (empty($id) || !is_numeric($id)) {
throw new InvalidArgumentException(__('Invalid ID passed.'));
throw new BadRequestException(__('Invalid ID passed.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
@ -145,18 +153,14 @@ class UserSettingsController extends AppController
if (!$checkAccess) {
throw new NotFoundException(__('Invalid user setting.'));
}
if ($this->_isRest()) {
unset($userSetting['User']);
return $this->RestResponse->viewData($userSetting, $this->response->type());
} else {
$this->set($data, $userSetting);
}
unset($userSetting['User']);
return $this->RestResponse->viewData($userSetting, $this->response->type());
}
public function setSetting($user_id = false, $setting = false)
{
if (!empty($setting)) {
if (!$this->UserSetting->checkSettingValidity($setting)) {
if (!$this->UserSetting->checkSettingValidity($setting) || $this->UserSetting->isInternal($setting)) {
throw new MethodNotAllowedException(__('Invalid setting.'));
}
$settingPermCheck = $this->UserSetting->checkSettingAccess($this->Auth->user(), $setting);
@ -177,10 +181,6 @@ class UserSettingsController extends AppController
if (!empty($setting)) {
$this->request->data['UserSetting']['setting'] = $setting;
}
// force our user's ID as the user ID in all cases
$userSetting = array(
'user_id' => $this->Auth->user('id')
);
$result = $this->UserSetting->setSetting($this->Auth->user(), $this->request->data);
if ($result) {
// if we've managed to save our setting
@ -213,29 +213,28 @@ class UserSettingsController extends AppController
if ($this->_isRest()) {
// GET request via the API should describe the endpoint
return $this->RestResponse->describe('UserSettings', 'setSetting', false, $this->response->type());
} else {
// load the valid settings from the model
if ($this->_isSiteAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'recursive' => -1,
'fields' => array('User.id', 'User.email')
));
} else if ($this->_isAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'recursive' => -1,
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
'fields' => array('User.id', 'User.email')
));
} else {
$users = array($this->Auth->user('id') => $this->Auth->user('email'));
}
if (!empty($user_id) && $this->request->is('get')) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
$this->set('setting', $setting);
$this->set('users', $users);
$this->set('validSettings', UserSetting::VALID_SETTINGS);
}
// load the valid settings from the model
if ($this->_isSiteAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'fields' => array('User.id', 'User.email')
));
} else if ($this->_isAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
'fields' => array('User.id', 'User.email')
));
} else {
$users = array($this->Auth->user('id') => $this->Auth->user('email'));
}
if (!empty($user_id) && $this->request->is('get')) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
$this->set('setting', $setting);
$this->set('users', $users);
$this->set('validSettings', $this->UserSetting->settingPlaceholders($this->Auth->user()));
$this->set('title_for_layout', __('Set User Setting'));
}
public function getSetting($userId = null, $setting = null)
@ -252,7 +251,7 @@ class UserSettingsController extends AppController
}
}
if (!$this->UserSetting->checkSettingValidity($setting)) {
if (!$this->UserSetting->checkSettingValidity($setting) || $this->UserSetting->isInternal($setting)) {
throw new NotFoundException(__('Invalid setting.'));
}
@ -365,7 +364,7 @@ class UserSettingsController extends AppController
'UserSetting' => array(
'user_id' => $this->Auth->user('id'),
'setting' => 'homepage',
'value' => json_encode(array('path' => $this->request->data['path']))
'value' => ['path' => $this->request->data['path']],
)
);
$result = $this->UserSetting->setSetting($this->Auth->user(), $setting);
@ -393,13 +392,13 @@ class UserSettingsController extends AppController
$hideColumns[] = $columnName;
}
$setting = array(
'UserSetting' => array(
$setting = [
'UserSetting' => [
'user_id' => $this->Auth->user()['id'],
'setting' => 'event_index_hide_columns',
'value' => json_encode($hideColumns)
)
);
'value' => $hideColumns,
]
];
$this->UserSetting->setSetting($this->Auth->user(), $setting);
return $this->RestResponse->saveSuccessResponse('UserSettings', 'eventIndexColumnToggle', false, 'json', 'Column visibility switched');
}

View File

@ -74,6 +74,7 @@ class UsersController extends AppController
} else {
$this->set('user', $user);
$this->set('admin_view', false);
$this->set('periodic_notifications', $this->User::PERIODIC_NOTIFICATIONS);
}
}
@ -118,6 +119,24 @@ class UsersController extends AppController
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Something went wrong, please try again later.')), 'status'=>200, 'type' => 'json'));
}
public function unsubscribe($code)
{
$user = $this->Auth->user();
if (!hash_equals($this->User->unsubscribeCode($user), rtrim($code, '.'))) {
$this->Flash->error(__('Invalid unsubscribe code.'));
$this->redirect(['action' => 'view', 'me']);
}
if ($user['autoalert']) {
$this->User->updateField($this->Auth->user(), 'autoalert', false);
$this->Flash->success(__('Successfully unsubscribed from event alert.'));
} else {
$this->Flash->info(__('Already unsubscribed from event alert.'));
}
$this->redirect(['action' => 'view', 'me']);
}
public function edit()
{
$currentUser = $this->User->find('first', array(
@ -154,7 +173,7 @@ class UsersController extends AppController
}
}
}
if (!$abortPost && !$this->_isRest()) {
if (!$abortPost && (!$this->_isRest() || empty($this->request->header('Authorization')))) {
if (Configure::read('Security.require_password_confirmation')) {
if (!empty($this->request->data['User']['current_password'])) {
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
@ -171,7 +190,7 @@ class UsersController extends AppController
}
if (!$abortPost) {
// What fields should be saved (allowed to be saved)
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled', 'date_modified');
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled', 'date_modified', 'notification_daily', 'notification_weekly', 'notification_monthly');
if ($this->__canChangeLogin()) {
$fieldList[] = 'email';
}
@ -427,6 +446,7 @@ class UsersController extends AppController
'expiration',
'current_login',
'last_login',
'last_api_access',
'force_logout',
'date_created',
'date_modified'
@ -450,6 +470,7 @@ class UsersController extends AppController
} else {
$this->set('urlparams', $urlParams);
$this->set('passedArgsArray', $passedArgsArray);
$this->set('periodic_notifications', $this->User::PERIODIC_NOTIFICATIONS);
$conditions = array();
if ($this->_isSiteAdmin()) {
$users = $this->paginate();
@ -541,7 +562,7 @@ class UsersController extends AppController
$rules = $this->_arrayToValuesIndexArray($rules);
$this->set('rules', $rules);
$this->set('baseurl', Configure::read('MISP.baseurl'));
$this->layout = 'ajax';
$this->layout = false;
}
public function admin_view($id = null)
@ -590,6 +611,7 @@ class UsersController extends AppController
$user2 = $this->User->find('first', array('conditions' => array('User.id' => $user['User']['invited_by']), 'recursive' => -1));
$this->set('id', $id);
$this->set('user2', $user2);
$this->set('periodic_notifications', $this->User::PERIODIC_NOTIFICATIONS);
$this->set('admin_view', true);
$this->render('view');
}
@ -631,21 +653,19 @@ class UsersController extends AppController
if (isset($this->request->data['User']['password'])) {
$this->request->data['User']['confirm_password'] = $this->request->data['User']['password'];
}
$default_publish_alert = Configure::check('MISP.default_publish_alert') ? Configure::read('MISP.default_publish_alert') : 0;
$defaults = array(
'external_auth_required' => 0,
'external_auth_key' => '',
'server_id' => 0,
'gpgkey' => '',
'certif_public' => '',
'autoalert' => $default_publish_alert,
'contactalert' => 0,
'disabled' => 0,
'newsread' => 0,
'change_pw' => 1,
'authkey' => (new RandomTool())->random_str(true, 40),
'termsaccepted' => 0,
'org_id' => $this->Auth->user('org_id')
'external_auth_required' => 0,
'external_auth_key' => '',
'server_id' => 0,
'gpgkey' => '',
'certif_public' => '',
'autoalert' => $this->User->defaultPublishAlert(),
'contactalert' => 0,
'disabled' => 0,
'newsread' => 0,
'change_pw' => 1,
'termsaccepted' => 0,
'org_id' => $this->Auth->user('org_id'),
);
foreach ($defaults as $key => $value) {
if (!isset($this->request->data['User'][$key])) {
@ -654,15 +674,14 @@ class UsersController extends AppController
}
}
$this->request->data['User']['date_created'] = time();
$this->request->data['User']['date_modified'] = time();
if (!array_key_exists($this->request->data['User']['role_id'], $syncRoles)) {
$this->request->data['User']['server_id'] = 0;
}
$this->User->create();
// set invited by
$this->loadModel('Role');
$this->Role->recursive = -1;
$chosenRole = $this->Role->findById($this->request->data['User']['role_id']);
$chosenRole = $this->User->Role->find('first', [
'conditions' => ['id' => $this->request->data['User']['role_id']],
]);
if (empty($chosenRole)) {
throw new MethodNotAllowedException('Invalid role');
}
@ -682,9 +701,6 @@ class UsersController extends AppController
$this->request->data['User']['newsread'] = 0;
if (!$this->_isSiteAdmin()) {
$this->request->data['User']['org_id'] = $this->Auth->user('org_id');
$this->loadModel('Role');
$this->Role->recursive = -1;
$chosenRole = $this->Role->findById($this->request->data['User']['role_id']);
if (
$chosenRole['Role']['perm_site_admin'] == 1 ||
$chosenRole['Role']['perm_regexp_access'] == 1 ||
@ -804,8 +820,7 @@ class UsersController extends AppController
$this->set('isSiteAdmin', $this->_isSiteAdmin());
$this->set('default_role_id', $default_role_id);
$this->set('servers', $servers);
$this->set(compact('roles'));
$this->set(compact('syncRoles'));
$this->set(compact('roles', 'syncRoles'));
}
}
@ -853,7 +868,7 @@ class UsersController extends AppController
$this->request->data['User'] = $this->request->data;
}
$abortPost = false;
if (!$this->_isRest()) {
if (!$this->_isRest() || empty($this->request->header('Authorization'))) {
if (Configure::read('Security.require_password_confirmation')) {
if (!empty($this->request->data['User']['current_password'])) {
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
@ -932,9 +947,9 @@ class UsersController extends AppController
$fields[] = 'role_id';
}
if (!$this->_isSiteAdmin() && isset($this->request->data['User']['role_id'])) {
$this->loadModel('Role');
$this->Role->recursive = -1;
$chosenRole = $this->Role->findById($this->request->data['User']['role_id']);
$chosenRole = $this->User->Role->find('first', [
'conditions' => ['id' => $this->request->data['User']['role_id']],
]);
if (empty($chosenRole) || (($chosenRole['Role']['id'] != $allowedRole) && ($chosenRole['Role']['perm_site_admin'] == 1 || $chosenRole['Role']['perm_regexp_access'] == 1 || $chosenRole['Role']['perm_sync'] == 1))) {
throw new Exception('You are not authorised to assign that role to a user.');
}
@ -1047,8 +1062,7 @@ class UsersController extends AppController
$this->set('servers', $servers);
$this->set('orgs', $orgs);
$this->set('id', $id);
$this->set(compact('roles'));
$this->set(compact('syncRoles'));
$this->set(compact('roles', 'syncRoles'));
$this->set('canChangeLogin', $this->__canChangeLogin());
$this->set('canChangePassword', $this->__canChangePassword());
}
@ -1058,9 +1072,6 @@ class UsersController extends AppController
if (!$this->request->is('post') && !$this->request->is('delete')) {
throw new MethodNotAllowedException(__('Action not allowed, post or delete request expected.'));
}
if (!$this->_isAdmin()) {
throw new Exception('Administrators only.');
}
$this->User->id = $id;
$conditions = array('User.id' => $id);
if (!$this->_isSiteAdmin()) {
@ -1383,16 +1394,16 @@ class UsersController extends AppController
public function histogram($selected = null)
{
//if (!$this->request->is('ajax') && !$this->_isRest()) throw new MethodNotAllowedException('This function can only be accessed via AJAX or the API.');
$user = $this->_closeSession();
if ($selected == '[]') {
$selected = null;
}
$selectedTypes = array();
if ($selected) {
$selectedTypes = json_decode($selected);
$selectedTypes = $this->_jsonDecode($selected);
}
if (!$this->_isSiteAdmin() && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
$org_ids = array($this->Auth->user('org_id'));
$org_ids = array($user['org_id']);
} else {
$org_ids = $this->User->Event->find('column', array(
'fields' => array('Event.orgc_id'),
@ -1403,12 +1414,7 @@ class UsersController extends AppController
'fields' => array('Organisation.id', 'Organisation.name'),
'conditions' => array('Organisation.id' => $org_ids)
));
$orgs = array(0 => 'All organisations');
foreach ($org_ids as $v) {
if (!empty($orgs_temp[$v])) {
$orgs[$v] = $orgs_temp[$v];
}
}
$orgs = array(0 => 'All organisations') + $orgs_temp;
$data = array();
$max = 1;
foreach ($orgs as $org_id => $org_name) {
@ -1481,7 +1487,7 @@ class UsersController extends AppController
$this->set('typeDb', $typeDb);
$this->set('sigTypes', $sigTypes);
$this->layout = 'ajax';
$this->layout = false;
}
public function terms()
@ -1491,17 +1497,36 @@ class UsersController extends AppController
$this->Flash->success(__('You accepted the Terms and Conditions.'));
$this->redirect(array('action' => 'routeafterlogin'));
}
$termsFile = Configure::read('MISP.terms_file');
if (empty($termsFile)) {
throw new NotFoundException(__("MISP Terms and Conditions are not defined"));
}
$termsDownload = (bool)Configure::read('MISP.terms_download');
if (!$termsDownload) {
$termsFilePath = APP . 'files' . DS . 'terms' . DS . basename($termsFile);
try {
$termsContent = FileAccessTool::readFromFile($termsFilePath);
} catch (Exception $e) {
$termsContent = false;
}
$this->set("termsContent", $termsContent);
}
$this->set("termsDownload", $termsDownload);
$this->set('termsaccepted', $this->Auth->user('termsaccepted'));
}
public function downloadTerms()
{
if (!Configure::read('MISP.terms_file')) {
$termsFile = APP ."View/Users/terms";
} else {
$termsFile = APP . 'files' . DS . 'terms' . DS . Configure::read('MISP.terms_file');
$termsFile = Configure::read('MISP.terms_file');
if (empty($termsFile)) {
throw new NotFoundException(__("MISP Terms and Conditions are not defined"));
}
$this->response->file($termsFile, array('download' => true, 'name' => Configure::read('MISP.terms_file')));
$termsFilePath = APP . 'files' . DS . 'terms' . DS . basename($termsFile);
$this->response->file($termsFilePath, ['download' => true, 'name' => $termsFile]);
return $this->response;
}
@ -1588,9 +1613,6 @@ class UsersController extends AppController
public function admin_email($isPreview=false)
{
if (!$this->_isAdmin()) {
throw new MethodNotAllowedException();
}
$isPostOrPut = $this->request->is('post') || $this->request->is('put');
$conditions = array();
if (!$this->_isSiteAdmin()) {
@ -1600,17 +1622,11 @@ class UsersController extends AppController
// harvest parameters
if ($isPostOrPut) {
$recipient = $this->request->data['User']['recipient'];
} else {
$recipient = isset($this->params['named']['recipient']) ? $this->params['named']['recipient'] : null;
}
if ($isPostOrPut) {
$recipientEmailList = $this->request->data['User']['recipientEmailList'];
} else {
$recipientEmailList = isset($this->params['named']['recipientEmailList']) ? $this->params['named']['recipientEmailList'] : null;
}
if ($isPostOrPut) {
$orgNameList = $this->request->data['User']['orgNameList'];
} else {
$recipient = isset($this->params['named']['recipient']) ? $this->params['named']['recipient'] : null;
$recipientEmailList = isset($this->params['named']['recipientEmailList']) ? $this->params['named']['recipientEmailList'] : null;
$orgNameList = isset($this->params['named']['orgNameList']) ? $this->params['named']['orgNameList'] : null;
}
@ -1637,7 +1653,7 @@ class UsersController extends AppController
$users = $this->User->find('all', array('recursive' => -1, 'order' => array('email ASC'), 'conditions' => $conditions));
// User has filled in his contact form, send out the email.
if ($isPostOrPut) {
$this->request->data['User']['message'] = $this->User->adminMessageResolve($this->request->data['User']['message']);
$this->request->data['User']['message'] = $this->__replaceEmailVariables($this->request->data['User']['message']);
$failures = '';
foreach ($users as $user) {
$password = $this->User->generateRandomPassword();
@ -1684,11 +1700,11 @@ class UsersController extends AppController
$textsToFetch = array('newUserText', 'passwordResetText');
$this->loadModel('Server');
foreach ($textsToFetch as $text) {
${$text} = Configure::read('MISP.' . $text);
if (!${$text}) {
${$text} = $this->Server->serverSettings['MISP'][$text]['value'];
$value = Configure::read('MISP.' . $text);
if (!$value) {
$value = $this->Server->serverSettings['MISP'][$text]['value'];
}
$this->set($text, ${$text});
$this->set($text, $value);
}
}
}
@ -1723,7 +1739,7 @@ class UsersController extends AppController
$error = 'No encryption key found for the user and the instance posture blocks non encrypted e-mails from being sent.';
}
$this->set('error', $error);
$this->layout = 'ajax';
$this->layout = false;
$this->set('user', $user);
$this->set('firstTime', $firstTime);
$this->render('ajax/passwordResetConfirmationForm');
@ -1780,9 +1796,7 @@ class UsersController extends AppController
// Email construction
$body = Configure::read('Security.email_otp_text') ?: $this->Server->serverSettings['Security']['email_otp_text']['value'];
$body = str_replace('$misp', Configure::read('MISP.baseurl'), $body);
$body = str_replace('$org', Configure::read('MISP.org'), $body);
$body = str_replace('$contact', Configure::read('MISP.contact'), $body);
$body = $this->__replaceEmailVariables($body);
$body = str_replace('$validity', $validity, $body);
$body = str_replace('$otp', $otp, $body);
$body = str_replace('$ip', $this->__getClientIP(), $body);
@ -2051,7 +2065,6 @@ class UsersController extends AppController
private function __statisticsUsers($params = array())
{
$this->loadModel('Organisation');
$this->loadModel('User');
$this_month = strtotime(date('Y/m') . '/01');
$this_year = strtotime(date('Y') . '/01/01');
$ranges = array(
@ -2103,12 +2116,13 @@ class UsersController extends AppController
public function tagStatisticsGraph()
{
$this->_closeSession();
$this->loadModel('EventTag');
$tags = $this->EventTag->getSortedTagList();
$this->loadModel('Taxonomy');
$taxonomies = $this->Taxonomy->find('list', array(
'conditions' => array('enabled' => true),
'fields' => array('Taxonomy.namespace')
'conditions' => array('enabled' => true),
'fields' => array('Taxonomy.namespace')
));
$flatData = array();
$tagIds = $this->EventTag->Tag->find('list', array('fields' => array('Tag.name', 'Tag.id')));
@ -2138,7 +2152,6 @@ class UsersController extends AppController
}
$treemap['children'][] = $newElement;
}
$taxonomyColourCodes = array();
$taxonomies = array_merge(array('custom'), $taxonomies);
if ($this->_isRest()) {
$data = array(
@ -2147,12 +2160,11 @@ class UsersController extends AppController
);
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->set('taxonomyColourCodes', $taxonomyColourCodes);
$this->set('taxonomies', $taxonomies);
$this->set('flatData', $flatData);
$this->set('treemap', $treemap);
$this->set('tags', $tags);
$this->layout = 'treemap';
$this->layout = false;
$this->render('ajax/tag_statistics_graph');
}
}
@ -2337,6 +2349,7 @@ class UsersController extends AppController
public function searchGpgKey($email = false)
{
session_abort();
if (!$email) {
throw new NotFoundException('No email provided.');
}
@ -2345,7 +2358,6 @@ class UsersController extends AppController
throw new NotFoundException('No keys found for given email at keyserver.');
}
$this->set('keys', $keys);
$this->autorender = false;
$this->layout = false;
$this->render('ajax/fetchpgpkey');
}
@ -2752,6 +2764,56 @@ class UsersController extends AppController
}
}
public function notificationSettings()
{
$user = $this->Auth->user();
if ($this->request->is('post') || $this->request->is('put')) {
$success = $this->User->saveNotificationSettings($user['id'], $this->request->data);
if ($success) {
$this->_refreshAuth();
$message = __('Notification settings saved');
$this->Flash->success($message);
$this->redirect(['action' => 'view', 'me']);
} else {
$message = __('Notification settings could not be saved');
$this->Flash->error($message);
}
}
$this->request->data = [
'User' => $user,
'periodic_settings' => $this->User->fetchPeriodicSettingForUser($user['id']),
];
$this->loadModel('Attribute');
$distributionData = $this->Attribute->fetchDistributionData($user);
unset($distributionData['levels'][5]);
$this->set('sharingGroups', $distributionData['sgs']);
$this->set('distributionLevels', $distributionData['levels']);
$conditions = $this->User->Organisation->createConditions($user);
$conditions['local'] = true;
$orgs = $this->User->Organisation->find('list', [
'conditions' => $conditions,
'fields' => ['id', 'name'],
'order' => 'name',
]);
$this->set('orgs', $orgs);
$this->set('user', $user);
$this->set('title_for_layout', __('Notification settings'));
}
public function viewPeriodicSummary(string $period)
{
$userId = $this->Auth->user('id');
$summary = $this->User->generatePeriodicSummary($userId, $period);
$periodicSettings = $this->User->fetchPeriodicSettingForUser($userId);
$this->set('periodic_settings', $periodicSettings);
$this->set('summary', $summary);
$this->set('period', $period);
$this->set('title_for_layout', __('Periodic summary'));
}
private function __canChangePassword()
{
return $this->ACL->canUserAccess($this->Auth->user(), 'users', 'change_pw');
@ -2764,4 +2826,17 @@ class UsersController extends AppController
}
return !Configure::read('MISP.disable_user_login_change');
}
/**
* Replaces $misp, $org and $contact variables in emails
* @param string $body
* @return string
*/
private function __replaceEmailVariables($body)
{
$body = str_replace('$misp', Configure::read('MISP.baseurl'), $body);
$body = str_replace('$org', Configure::read('MISP.org'), $body);
$body = str_replace('$contact', Configure::read('MISP.contact'), $body);
return $body;
}
}

View File

@ -321,7 +321,7 @@ class WarninglistsController extends AppController
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action is available via AJAX only.'));
}
$this->layout = 'ajax';
$this->layout = false;
$this->render('ajax/getToggleField');
}
@ -360,9 +360,7 @@ class WarninglistsController extends AppController
public function import()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This function only accepts POST requests.'));
}
$this->request->allowMethod(['post']);
if (empty($this->request->data)) {
throw new BadRequestException(__('No valid data received.'));
@ -378,11 +376,11 @@ class WarninglistsController extends AppController
throw new BadRequestException(__('No valid data received: `list` field is not array'));
}
$id = $this->Warninglist->import($this->request->data);
if (is_int($id)) {
try {
$id = $this->Warninglist->import($this->request->data);
return $this->RestResponse->saveSuccessResponse('Warninglist', 'import', $id, false, __('Warninglist imported'));
} else {
return $this->RestResponse->saveFailResponse('Warninglist', 'import', false, $id);
} catch (Exception $e) {
return $this->RestResponse->saveFailResponse('Warninglist', 'import', false, $e->getMessage());
}
}
@ -417,15 +415,14 @@ class WarninglistsController extends AppController
public function delete($id)
{
if ($this->request->is('post')) {
$id = intval($id);
$id = (int)$id;
$result = $this->Warninglist->quickDelete($id);
if ($result) {
$this->Flash->success(__('Warninglist successfully deleted.'));
$this->redirect(array('controller' => 'warninglists', 'action' => 'index'));
} else {
$this->Flash->error(__('Warninglists could not be deleted.'));
$this->redirect(array('controller' => 'warninglists', 'action' => 'index'));
$this->Flash->error(__('Warninglist could not be deleted.'));
}
$this->redirect(['controller' => 'warninglists', 'action' => 'index']);
} else {
if ($this->request->is('ajax')) {
$this->set('id', $id);

View File

@ -0,0 +1,127 @@
<?php
App::uses('AppController', 'Controller');
class WorkflowBlueprintsController extends AppController
{
public $components = array(
'RequestHandler'
);
public function update($force = false)
{
$this->request->allowMethod(['post', 'put']);
$this->WorkflowBlueprint->update($force);
$message = __('Default workflow blueprints updated');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('WorkflowBlueprint', 'update', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(array('controller' => 'workflowBlueprints', 'action' => 'index'));
}
}
public function index()
{
$params = [
'filters' => ['name', 'uuid', 'timestamp'],
'quickFilters' => ['name', 'uuid'],
];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('menuData', ['menuList' => 'workflowBlueprints', 'menuItem' => 'index']);
}
public function add($fromEditor = false)
{
$params = [
'beforeSave' => function(array $blueprint) {
$blueprint['WorkflowBlueprint']['default'] = false;
return $blueprint;
},
];
$this->CRUD->add($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('fromEditor', !empty($fromEditor));
$this->set('menuData', ['menuList' => 'workflowBlueprints', 'menuItem' => 'add']);
}
public function edit($id)
{
$params = [
'beforeSave' => function (array $blueprint) {
$blueprint['WorkflowBlueprint']['default'] = false;
return $blueprint;
},
];
$this->CRUD->edit($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->request->data['WorkflowBlueprint']['data'] = JsonTool::encode($this->data['WorkflowBlueprint']['data']);
$this->set('menuData', ['menuList' => 'workflowBlueprints', 'menuItem' => 'edit']);
$this->set('id', $id);
$this->render('add');
}
public function delete($id)
{
$params = [
];
$this->CRUD->delete($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('menuData', ['menuList' => 'workflowBlueprints', 'menuItem' => 'delete']);
}
public function view($id)
{
$filters = $this->IndexFilter->harvestParameters(['format']);
if (!empty($filters['format'])) {
if ($filters['format'] == 'dot') {
$dot = $this->WorkflowBlueprint->getDotNotation($id);
return $this->RestResponse->viewData($dot, $this->response->type());
} else if ($filters['format'] == 'mermaid') {
$mermaid = $this->WorkflowBlueprint->getMermaid($id);
return $this->RestResponse->viewData($mermaid, $this->response->type());
}
}
$this->CRUD->view($id, [
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('id', $id);
$this->set('menuData', ['menuList' => 'workflowBlueprints', 'menuItem' => 'view']);
}
public function import()
{
if ($this->request->is('post') || $this->request->is('put')) {
$workflowBlueprintData = JsonTool::decode($this->request->data['WorkflowBlueprint']['data']);
if ($workflowBlueprintData === null) {
throw new MethodNotAllowedException(__('Error while decoding JSON'));
}
$this->request->data['WorkflowBlueprint']['data'] = JsonTool::encode($workflowBlueprintData);
$this->add();
}
}
public function export($id)
{
$workflowBlueprint = $this->WorkflowBlueprint->find('first', [
'conditions' => [
'id' => $id,
]
]);
$content = JsonTool::encode($workflowBlueprint, JSON_PRETTY_PRINT);
$this->response->body($content);
$this->response->type('json');
$this->response->download(sprintf('blueprint_%s_%s.json', str_replace(' ', '-', strtolower($workflowBlueprint['WorkflowBlueprint']['name'])), time()));
return $this->response;
}
}

View File

@ -0,0 +1,438 @@
<?php
App::uses('AppController', 'Controller');
class WorkflowsController extends AppController
{
public $components = array(
'RequestHandler'
);
private $toggleableFields = ['enabled'];
public function beforeFilter()
{
parent::beforeFilter();
$this->Security->unlockedActions[] = 'checkGraph';
$this->Security->unlockedActions[] = 'moduleStatelessExecution';
$requirementErrors = [];
if (empty(Configure::read('MISP.background_jobs'))) {
$requirementErrors[] = __('Background workers must be enabled to use workflows');
$this->render('error');
}
if (empty(Configure::read('Plugin.Workflow_enable'))) {
$requirementErrors[] = __('The workflow plugin must be enabled to use workflows. Go to `/servers/serverSettings/Plugin` the enable the `Plugin.Workflow` setting');
$this->render('error');
}
try {
$this->Workflow->setupRedisWithException();
} catch (Exception $e) {
$requirementErrors[] = $e->getMessage();
}
if (!empty($requirementErrors)) {
$this->set('requirementErrors', $requirementErrors);
$this->render('error');
}
}
public function index()
{
$params = [
'filters' => ['name', 'uuid'],
'quickFilters' => ['name', 'uuid'],
];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'index'));
}
public function rebuildRedis()
{
$this->Workflow->rebuildRedis();
}
public function edit($id)
{
$this->set('id', $id);
$savedWorkflow = $this->Workflow->fetchWorkflow($id);
if ($this->request->is('post') || $this->request->is('put')) {
$newWorkflow = $this->request->data;
$newWorkflow['Workflow']['data'] = JsonTool::decode($newWorkflow['Workflow']['data']);
$newWorkflow = $this->__applyDataFromSavedWorkflow($newWorkflow, $savedWorkflow);
$result = $this->Workflow->editWorkflow($newWorkflow);
$redirectTarget = ['action' => 'view', $id];
if (!empty($result['errors'])) {
return $this->__getFailResponseBasedOnContext($result['errors'], null, 'edit', $this->Workflow->id, $redirectTarget);
} else {
$successMessage = __('Workflow saved.');
$savedWorkflow = $result['saved'];
return $this->__getSuccessResponseBasedOnContext($successMessage, $savedWorkflow, 'edit', false, $redirectTarget);
}
} else {
$savedWorkflow['Workflow']['data'] = JsonTool::encode($savedWorkflow['Workflow']['data']);
$this->request->data = $savedWorkflow;
}
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'edit'));
$this->render('add');
}
public function delete($id)
{
$params = [
];
$this->CRUD->delete($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function view($id)
{
$filters = $this->IndexFilter->harvestParameters(['format']);
if (!empty($filters['format'])) {
if ($filters['format'] == 'dot') {
$dot = $this->Workflow->getDotNotation($id);
return $this->RestResponse->viewData($dot, $this->response->type());
} else if ($filters['format'] == 'mermaid') {
$mermaid = $this->Workflow->getMermaid($id);
return $this->RestResponse->viewData($mermaid, $this->response->type());
}
}
$this->CRUD->view($id, [
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('id', $id);
$this->set('menuData', array('menuList' => 'workflows', 'menuItem' => 'view'));
}
public function editor($id)
{
$trigger_id = false;
$workflow = false;
if (is_numeric($id)) {
$workflow_id = $id;
} else {
$trigger_id = $id;
}
$modules = $this->Workflow->getModulesByType();
if (!empty($trigger_id)) {
$trigger_ids = Hash::extract($modules['modules_trigger'], '{n}.id');
if (!in_array($trigger_id, $trigger_ids)) {
return $this->__getFailResponseBasedOnContext(
[__('Unkown trigger %s', $trigger_id)],
null,
'add',
$trigger_id,
['controller' => 'workflows', 'action' => 'triggers']
);
}
$workflow = $this->Workflow->fetchWorkflowByTrigger($trigger_id, false);
if (empty($workflow)) { // Workflow do not exists yet. Create it.
$result = $this->Workflow->addWorkflow([
'name' => sprintf('Workflow for trigger %s', $trigger_id),
'data' => $this->Workflow->genGraphDataForTrigger($trigger_id),
'trigger_id' => $trigger_id,
]);
if (!empty($result['errors'])) {
return $this->__getFailResponseBasedOnContext(
[__('Could not create workflow for trigger %s', $trigger_id), $result['errors']],
null,
'add',
$trigger_id,
['controller' => 'workflows', 'action' => 'editor']
);
}
$workflow = $this->Workflow->fetchWorkflowByTrigger($trigger_id, false);
}
} else {
$workflow = $this->Workflow->fetchWorkflow($workflow_id);
}
$modules = $this->Workflow->attachNotificationToModules($modules, $workflow);
$this->loadModel('WorkflowBlueprint');
$workflowBlueprints = $this->WorkflowBlueprint->find('all');
$workflowBlueprints = array_map(function($blueprint) {
return $this->WorkflowBlueprint->attachModuleDataToBlueprint($blueprint);
}, $workflowBlueprints);
$this->set('selectedWorkflow', $workflow);
$this->set('workflowTriggerId', $trigger_id);
$this->set('modules', $modules);
$this->set('workflowBlueprints', $workflowBlueprints);
}
public function executeWorkflow($workflow_id)
{
if ($this->request->is('post') || $this->request->is('put')) {
$blockingErrors = [];
$data = JsonTool::decode($this->request->data['Workflow']['data']);
$result = $this->Workflow->executeWorkflow($workflow_id, $data, $blockingErrors);
if (!empty($logging) && empty($result['success'])) {
$logging['message'] = !empty($logging['message']) ? $logging['message'] : __('Error while executing workflow.');
$errorMessage = implode(', ', $blockingErrors);
$this->Workflow->loadLog()->createLogEntry('SYSTEM', $logging['action'], $logging['model'], $logging['id'], $logging['message'], __('Returned message: %s', $errorMessage));
}
return $this->RestResponse->viewData([
'success' => $result['success'],
'outcome' => $result['outcomeText'],
], $this->response->type());
}
$this->render('ajax/executeWorkflow');
}
public function triggers()
{
$triggers = $this->Workflow->getModulesByType('trigger');
$triggers = $this->Workflow->attachWorkflowToTriggers($triggers);
$data = $triggers;
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$customPagination->truncateAndPaginate($data, $this->params, 'Workflow', true);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
}
$this->set('data', $data);
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'index_trigger']);
}
public function moduleIndex()
{
$modules = $this->Workflow->getModulesByType();
$errorWhileLoading = $this->Workflow->getModuleLoadingError();
$this->Module = ClassRegistry::init('Module');
$mispModules = $this->Module->getModules('Action');
$this->set('module_service_error', !is_array($mispModules));
$filters = $this->IndexFilter->harvestParameters(['type', 'actiontype', 'enabled']);
$moduleType = $filters['type'] ?? 'action';
$actionType = $filters['actiontype'] ?? 'all';
$enabledState = $filters['enabled'] ?? false;
if ($moduleType == 'all' || $moduleType == 'custom') {
$data = array_merge(
$modules["modules_action"],
$modules["modules_logic"]
);
} else {
$data = $modules["modules_{$moduleType}"];
}
if ($actionType == 'mispmodule') {
$data = array_filter($data, function($module) {
return !empty($module['is_misp_module']);
});
} else if ($actionType == 'blocking') {
$data = array_filter($data, function ($module) {
return !empty($module['blocking']);
});
} else if ($moduleType == 'custom') {
$data = array_filter($data, function ($module) {
return !empty($module['is_custom']);
});
}
if ($enabledState !== false) {
$moduleType = !empty($enabledState) ? 'enabled' : 'disabled';
$data = array_filter($data, function ($module) use ($enabledState) {
return !empty($enabledState) ? empty($module['disabled']) : !empty($module['disabled']);
});
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
}
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$params = $customPagination->createPaginationRules($data, $this->passedArgs, 'Workflow');
$params = $customPagination->applyRulesOnArray($data, $params, 'Workflow');
$params['options'] = array_merge($params['options'], $filters);
$this->params['paging'] = [$this->modelClass => $params];
$this->set('data', $data);
$this->set('indexType', $moduleType);
$this->set('actionType', $actionType);
$this->set('errorWhileLoading', $errorWhileLoading);
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'index_module']);
}
public function moduleView($module_id)
{
$module = $this->Workflow->getModuleByID($module_id);
if (empty($module)) {
throw new NotFoundException(__('Invalid trigger ID'));
}
$is_trigger = $module['module_type'] == 'trigger';
if ($is_trigger) {
$module = $this->Workflow->attachWorkflowToTriggers([$module])[0];
$module['listening_workflows'] = $this->Workflow->getListeningWorkflowForTrigger($module);
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($module, $this->response->type());
}
$this->set('data', $module);
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'view_module']);
}
public function toggleModule($module_id, $enabled, $is_trigger=false)
{
$this->request->allowMethod(['post', 'put']);
$saved = $this->Workflow->toggleModule($module_id, $enabled, $is_trigger);
if ($saved) {
return $this->__getSuccessResponseBasedOnContext(
__('%s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
null,
'toggle_module',
$module_id,
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
);
} else {
return $this->__getFailResponseBasedOnContext(
__('Could not %s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
null,
'toggle_module',
$module_id,
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
);
}
}
public function debugToggleField($workflow_id, $enabled)
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action is available via AJAX only.'));
}
$this->layout = false;
$this->render('ajax/getDebugToggleField');
if ($this->request->is('post') || $this->request->is('put')) {
$success = $this->Workflow->toggleDebug($workflow_id, $enabled);
if (!empty($success)) {
return $this->__getSuccessResponseBasedOnContext(
__('%s debug mode', ($enabled ? __('Enabled') : __('Disabled'))),
null,
'toggle_debug',
$workflow_id,
['action' => 'triggers']
);
} else {
return $this->__getFailResponseBasedOnContext(
__('Could not %s debug mode', ($enabled ? __('enable') : __('disable'))),
null,
'toggle_debug',
$workflow_id,
['action' => 'triggers']
);
}
}
}
public function massToggleField($fieldName, $enabled, $is_trigger=false)
{
if (!in_array($fieldName, $this->toggleableFields)) {
throw new MethodNotAllowedException(__('The field `%s` cannot be toggled', $fieldName));
}
if ($this->request->is('post') || $this->request->is('put')) {
$module_ids = JsonTool::decode($this->request->data['Workflow']['module_ids']);
$enabled_count = $this->Workflow->toggleModules($module_ids, $enabled, $is_trigger);
if (!empty($enabled_count)) {
return $this->__getSuccessResponseBasedOnContext(
__('%s %s modules', ($enabled ? 'Enabled' : 'Disabled'), $enabled_count),
null,
'toggle_module',
$module_ids,
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
);
} else {
return $this->__getFailResponseBasedOnContext(
__('Could not %s modules', ($enabled ? 'enable' : 'disable')),
null,
'toggle_module',
$module_ids,
['action' => (!empty($is_trigger) ? 'triggers' : 'moduleIndex')]
);
}
}
}
private function __getSuccessResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
{
if ($this->_isRest()) {
if (!is_null($data)) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
return $this->RestResponse->saveSuccessResponse('Workflow', $action, $id, false, $message);
}
} elseif ($this->request->is('ajax')) {
return $this->RestResponse->saveSuccessResponse('Workflow', $action, $id, false, $message, $data);
} else {
$this->Flash->success($message);
$this->redirect($redirect);
}
return;
}
private function __getFailResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
{
if (is_array($message)) {
$message = implode(', ', $message);
}
if ($this->_isRest()) {
if ($data !== null) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
return $this->RestResponse->saveFailResponse('Workflow', $action, $id, $message);
}
} elseif ($this->request->is('ajax')) {
return $this->RestResponse->saveFailResponse('Workflow', $action, $id, $message, false, $data);
} else {
$this->Flash->error($message);
$this->redirect($redirect);
}
}
private function __applyDataFromSavedWorkflow($newWorkflow, $savedWorkflow)
{
if (!isset($newWorkflow['Workflow'])) {
$newWorkflow = ['Workflow' => $newWorkflow];
}
$ignoreFieldList = ['id', 'uuid'];
foreach (Workflow::CAPTURE_FIELDS_EDIT as $field) {
if (!in_array($field, $ignoreFieldList) && isset($newWorkflow['Workflow'][$field])) {
$savedWorkflow['Workflow'][$field] = $newWorkflow['Workflow'][$field];
}
}
return $savedWorkflow;
}
public function checkGraph()
{
$this->request->allowMethod(['post']);
$graphData = JsonTool::decode($this->request->data['graph']);
$cycles = [];
$isAcyclic = $this->Workflow->workflowGraphTool->isAcyclic($graphData, $cycles);
$edgesMultipleOutput = [];
$hasMultipleOutputConnection = $this->Workflow->workflowGraphTool->hasMultipleOutputConnection($graphData, $edgesMultipleOutput);
$edgesWarnings = [];
$hasPathWarnings = $this->Workflow->hasPathWarnings($graphData, $edgesWarnings);
$data = [
'is_acyclic' => [
'is_acyclic' => $isAcyclic,
'cycles' => $cycles,
],
'multiple_output_connection' => [
'has_multiple_output_connection' => $hasMultipleOutputConnection,
'edges' => $edgesMultipleOutput,
],
'path_warnings' => [
'has_path_warnings' => $hasPathWarnings,
'edges' => $edgesWarnings,
],
];
return $this->RestResponse->viewData($data, 'json');
}
public function moduleStatelessExecution($module_id)
{
$this->request->allowMethod(['post']);
$input_data = JsonTool::decode($this->request->data['input_data']);
$param_data = $this->request->data['module_indexed_param'];
$result = $this->Workflow->moduleStatelessExecution($module_id, $input_data, $param_data);
return $this->RestResponse->viewData($result, 'json');
}
}

View File

@ -43,7 +43,7 @@ class RecentSightingsWidget
else $type = "Expiration";
$output = $attribute->{'value'} . " (id: " . $attribute->{'id'} . ") in " . $event->{'info'} . " (id: " . $event->{'id'} . ")";
$data[] = array( 'title' => __($type), 'value' => $output,
$data[] = array( 'title' => $type, 'value' => $output,
'html' => sprintf(
' (Event <a href="%s%s">%s</a>)',
Configure::read('MISP.baseurl') . '/events/view/', $event->{'id'},

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,73 @@
<?php
class DefaultWarning
{
public $description = '';
public $name = '';
public $functions = [
'emptyEventCheck',
'contextCheck',
'tlpDistributionCheck',
'taxonomyInconsistenciesCheck'
];
function __construct()
{
$this->name = __('Default');
$this->description = __('The default set of warnings included with MISP');
}
public function emptyEventCheck(array $event, array &$warnings)
{
if (empty($event['Attribute']) && empty($event['objects'])) {
$warnings[__('Content')][] = __('Your event has neither attributes nor objects, whilst this can have legitimate reasons (such as purely creating an event with an event report or galaxy clusters), in most cases it\'s a sign that the event has yet to be fleshed out.');
}
}
public function contextCheck(array $event, array &$warnings)
{
if (empty($event['Galaxy']) && empty($event['EventTag'])) {
$warnings[__('Contextualisation')][] = __('Your event has neither tags nor galaxy clusters attached - generally adding context to an event allows for quicker decision making and more accurate filtering, it is highly recommended that you label your events to the best of your ability.');
}
}
public function tlpDistributionCheck(array $event, array &$warnings)
{
if (!empty($event['EventTag'])) {
foreach ($event['EventTag'] as $eT) {
$tagName = $eT['Tag']['name'];
$this->__tlpTaxonomyCheck($tagName, $warnings);
if ($tagName === 'tlp:white' && $event['Event']['distribution'] != Event::DISTRIBUTION_ALL) {
$warnings[__('Distribution')][] = __('The event is tagged as tlp:white, yet the distribution is not set to all. Change the distribution setting to something more lax if you wish for the event to propagate further.');
} else if ($tagName === 'tlp:green' && !in_array($event['Event']['distribution'], [Event::DISTRIBUTION_COMMUNITY, Event::DISTRIBUTION_CONNECTED, Event::DISTRIBUTION_ALL])) {
$warnings[__('Distribution')][] = __('The event is tagged as tlp:green, yet the distribution is not set to community, connected communities or all. tlp:green assumes sharing with your entire community - make sure that the selected distribution setting covers that.');
} else if (in_array($tagName, ['tlp:amber', 'tlp:red'], true) && $event['Event']['distribution'] == Event::DISTRIBUTION_ALL) {
$warnings[__('Distribution')][] = __('The event is tagged as %s, yet the distribution is set to all, be aware of potential information leakage.', $tagName);
}
}
}
}
public function taxonomyInconsistenciesCheck(array $event, array &$warnings)
{
if (Configure::read('MISP.disable_taxonomy_consistency_checks')) {
$warnings[__('Tags')][] = __('Taxonomy consistency checks are disabled in the configuration, set `MISP.disable_taxonomy_consistency_checks` to `false` to enable them.');
}
}
/**
* @param string $tagName
* @return void
*/
private function __tlpTaxonomyCheck($tagName, array &$warnings)
{
$lowerTagName = trim(strtolower($tagName));
if (substr($lowerTagName, 0, 4) === 'tlp:') {
if (!in_array($lowerTagName, ['tlp:white', 'tlp:green', 'tlp:amber', 'tlp:red', 'tlp:ex:chr', 'tlp:clear', 'tlp:amber+strict'], true)) {
$warnings['TLP'][] = __('Unknown TLP tag, please refer to the TLP taxonomy as to what is valid, otherwise filtering rules created by your partners may miss your intent.');
} else if ($lowerTagName !== $tagName) {
$warnings['TLP'][] = __('TLP tag with invalid formatting: Make sure that you only use TLP tags from the taxonomy. Custom tags with invalid capitalisation, white spaces or other artifacts will break synchronisation and filtering rules intended for the correct taxonomy derived tags.');
}
}
}
}

View File

@ -127,7 +127,7 @@ class AttackExport
$result['galaxyId'] = $this->__galaxy_id;
$matrixGalaxies = $this->__GalaxyModel->getAllowedMatrixGalaxies();
$result['matrixGalaxies'] = $matrixGalaxies;
return json_encode($result);
return JsonTool::encode($result);
}
public function separator()

View File

@ -0,0 +1,194 @@
<?php
class ContextExport
{
public $additional_params = [
'flatten' => 1,
'includeEventTags' => 1,
'includeGalaxy' => 1,
'noSightings' => true,
'noEventReports' => true,
'noShadowAttributes' => true,
'sgReferenceOnly' => true,
'includeEventCorrelations' => false,
'fetchFullClusters' => false,
];
private $__eventTags = [];
/** @var array Tag name => Galaxy */
private $__eventGalaxies = [];
private $__aggregatedTags = [];
private $__aggregatedClusters = [];
private $__taxonomyFetched = [];
private $__passedOptions = [];
public $non_restrictive_export = true;
public $renderView = 'context_view';
/** @var AttackExport */
private $AttackExport;
/** @var Taxonomy */
private $Taxonomy;
/** @var Galaxy */
private $Galaxy;
public function header($options = array())
{
$this->Taxonomy = ClassRegistry::init('Taxonomy');
$this->Galaxy = ClassRegistry::init('Galaxy');
App::uses('AttackExport', 'Export');
$this->AttackExport = new AttackExport();
$this->__passedOptions = $options;
return '';
}
public function handler($data, $options = array())
{
$this->__aggregate($data, Hash::extract($data, 'EventTag.{n}.Tag'));
if (!empty($data['Attribute'])) {
foreach ($data['Attribute'] as $attribute) {
$this->__aggregate($attribute, Hash::extract($attribute, 'AttributeTag.{n}.Tag'));
}
}
$this->AttackExport->handler($data, $options);
return '';
}
public function footer()
{
$attackFinal = $this->AttackExport->footer();
$this->__aggregateTagsPerTaxonomy();
$this->__aggregateClustersPerGalaxy();
$attackData = $attackFinal === '' ? [] : JsonTool::decode($attackFinal);
if (!empty($this->__passedOptions['filters']['staticHtml'])) {
$attackData['static'] = true;
}
return JsonTool::encode([
'attackData' => $attackData,
'tags' => $this->__aggregatedTags,
'clusters' => $this->__aggregatedClusters,
]);
}
public function separator()
{
return '';
}
private function __aggregate(array $entity, array $tags)
{
if (!empty($entity['Galaxy'])) {
foreach ($entity['Galaxy'] as $galaxy) {
foreach ($galaxy['GalaxyCluster'] as $galaxyCluster) {
$this->__eventGalaxies[$galaxyCluster['tag_name']] = $galaxyCluster;
}
}
}
if (!empty($tags)) {
foreach ($tags as $tag) {
if (strpos($tag['name'], 'misp-galaxy:') === 0) {
continue;
}
$this->__eventTags[$tag['name']] = $tag;
$this->fetchTaxonomyForTag($tag['name']);
}
}
}
/**
* @param string $tagName
* @return void
* @throws RedisException
*/
private function fetchTaxonomyForTag($tagName)
{
$splits = $this->Taxonomy->splitTagToComponents($tagName);
if ($splits === null) {
return; // tag is not taxonomy tag
}
if (!isset($this->__taxonomyFetched[$splits['namespace']])) {
$fetchedTaxonomy = $this->Taxonomy->getTaxonomyForTag($tagName, false, true);
if (!empty($fetchedTaxonomy)) {
$fetched = [
'Taxonomy' => $fetchedTaxonomy['Taxonomy'],
'TaxonomyPredicate' => [],
];
foreach ($fetchedTaxonomy['TaxonomyPredicate'] as $predicate) {
$fetched['TaxonomyPredicate'][$predicate['value']] = $predicate;
if (!empty($predicate['TaxonomyEntry'])) {
$fetched['TaxonomyPredicate'][$predicate['value']]['TaxonomyEntry'] = [];
foreach ($predicate['TaxonomyEntry'] as $entry) {
$fetched['TaxonomyPredicate'][$predicate['value']]['TaxonomyEntry'][$entry['value']] = $entry;
}
}
}
$this->__taxonomyFetched[$splits['namespace']] = $fetched;
} else {
// Do not try to fetch non existing taxonomy again
$this->__taxonomyFetched[$splits['namespace']] = false;
}
}
}
private function __aggregateTagsPerTaxonomy()
{
ksort($this->__eventTags);
foreach ($this->__eventTags as $tagname => $tagData) {
$splits = $this->Taxonomy->splitTagToComponents($tagname);
if ($splits === null) {
$this->__aggregatedTags['Custom Tags'][]['Tag'] = $tagData;
continue;
}
$taxonomy = [];
if (!empty($this->__taxonomyFetched[$splits['namespace']])) {
$taxonomy = $this->__taxonomyFetched[$splits['namespace']];
}
if (!empty($taxonomy['TaxonomyPredicate'][$splits['predicate']])) {
$predicate = $taxonomy['TaxonomyPredicate'][$splits['predicate']];
$entry = null;
if (!empty($splits['value']) && isset($predicate['TaxonomyEntry'][$splits['value']])) {
$entry = $predicate['TaxonomyEntry'][$splits['value']];
}
unset($predicate['TaxonomyEntry']);
$this->__aggregatedTags[$splits['namespace']][] = [
'Taxonomy' => $taxonomy['Taxonomy'],
'TaxonomyPredicate' => $predicate,
'TaxonomyEntry' => $entry,
'Tag' => $tagData,
];
} else {
$this->__aggregatedTags['Custom Tags'][]['Tag'] = $tagData;
}
}
}
private function __aggregateClustersPerGalaxy()
{
$galaxyTypes = [];
foreach ($this->__eventGalaxies as $tagName => $foo) {
$splits = $this->Taxonomy->splitTagToComponents($tagName);
$galaxyTypes[$splits['predicate']] = true;
}
$fetchedGalaxies = $this->Galaxy->find('all', [
'recursive' => -1,
'conditions' => array('Galaxy.type' => array_keys($galaxyTypes)),
]);
$fetchedGalaxies = array_column(array_column($fetchedGalaxies, 'Galaxy'), null, 'type');
ksort($this->__eventGalaxies);
foreach ($this->__eventGalaxies as $tagName => $cluster) {
$splits = $this->Taxonomy->splitTagToComponents($tagName);
$galaxy = $fetchedGalaxies[$splits['predicate']];
$this->__aggregatedClusters[$splits['predicate']][] = [
'Galaxy' => $galaxy,
'GalaxyCluster' => $cluster,
];
}
}
}

View File

@ -0,0 +1,7 @@
<?php
App::uses('ContextExport', 'Export');
class ContextMarkdownExport extends ContextExport
{
public $renderView = 'context_markdown_view';
}

View File

@ -6,16 +6,16 @@ class HashesExport
'flatten' => 1
);
public $validTypes = array(
const VALID_TYPES = array(
'simple' => array(
'md5', 'sha1', 'sha256', 'sha224', 'sha512', 'sha512/224', 'sha512/256', 'ssdeep', 'imphash', 'tlsh',
'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'pehash', 'authentihash',
'impfuzzy'
'md5', 'sha1', 'sha256', 'sha224', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256',
'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'tlsh', 'x509-fingerprint-sha1', 'x509-fingerprint-md5',
'x509-fingerprint-sha256', 'pehash', 'authentihash', 'impfuzzy'
),
'composite' => array(
'malware-sample', 'filename|md5', 'filename|sha1', 'filename|sha256', 'filename|sha224', 'filename|sha512',
'filename|sha512/224', 'filename|sha512/256', 'filename|ssdeep', 'filename|imphash', 'filename|tlsh',
'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'filename|pehash',
'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384',
'filename|sha3-512', 'filename|ssdeep', 'filename|imphash', 'filename|tlsh', 'filename|pehash',
'filename|authentihash', 'filename|impfuzzy'
)
);
@ -23,18 +23,17 @@ class HashesExport
public function handler($data, $options = array())
{
if ($options['scope'] === 'Attribute') {
if (in_array($data['Attribute']['type'], $this->validTypes['composite'])) {
if (in_array($data['Attribute']['type'], self::VALID_TYPES['composite'], true)) {
return explode('|', $data['Attribute']['value'])[1];
} else if (in_array($data['Attribute']['type'], $this->validTypes['simple'])) {
} else if (in_array($data['Attribute']['type'], self::VALID_TYPES['simple'], true)) {
return $data['Attribute']['value'];
}
}
if ($options['scope'] === 'Event') {
} else if ($options['scope'] === 'Event') {
$result = array();
foreach ($data['Attribute'] as $attribute) {
if (in_array($attribute['type'], $this->validTypes['composite'])) {
if (in_array($attribute['type'], self::VALID_TYPES['composite'], true)) {
$result[] = explode('|', $attribute['value'])[1];
} else if (in_array($attribute['type'], $this->validTypes['simple'])) {
} else if (in_array($attribute['type'], self::VALID_TYPES['simple'], true)) {
$result[] = $attribute['value'];
}
}

View File

@ -2,7 +2,7 @@
class JsonExport
{
public $non_restrictive_export = true;
public $non_restrictive_export = true;
/**
* @param $data
@ -11,17 +11,17 @@ class JsonExport
*/
public function handler($data, $options = array())
{
if ($options['scope'] === 'Attribute') {
return $this->__attributeHandler($data, $options);
} else if($options['scope'] === 'Event') {
return $this->__eventHandler($data, $options);
} else if($options['scope'] === 'Object') {
if ($options['scope'] === 'Attribute') {
return $this->__attributeHandler($data, $options);
} else if ($options['scope'] === 'Event') {
return $this->__eventHandler($data, $options);
} else if ($options['scope'] === 'Object') {
return $this->__objectHandler($data, $options);
} else if($options['scope'] === 'Sighting') {
return $this->__sightingsHandler($data, $options);
} else if($options['scope'] === 'GalaxyCluster') {
return $this->__galaxyClusterHandler($data, $options);
}
} else if ($options['scope'] === 'Sighting') {
return $this->__sightingsHandler($data, $options);
} else if ($options['scope'] === 'GalaxyCluster') {
return $this->__galaxyClusterHandler($data, $options);
}
}
/**
@ -29,66 +29,68 @@ class JsonExport
* @param array $options
* @return Generator
*/
private function __eventHandler($event, $options = array())
private function __eventHandler($event, $options = array())
{
App::uses('JSONConverterTool', 'Tools');
return JSONConverterTool::streamConvert($event);
}
private function __objectHandler($object, $options = array()) {
App::uses('JSONConverterTool', 'Tools');
return json_encode(JSONConverterTool::convertObject($object, false, true));
return JSONConverterTool::streamConvert($event);
}
private function __attributeHandler($attribute, $options = array())
{
$attribute = array_merge($attribute['Attribute'], $attribute);
unset($attribute['Attribute']);
if (isset($attribute['Object']) && empty($attribute['Object']['id'])) {
unset($attribute['Object']);
}
$tagTypes = array('AttributeTag', 'EventTag');
foreach($tagTypes as $tagType) {
if (isset($attribute[$tagType])) {
foreach ($attribute[$tagType] as $tk => $tag) {
if ($tagType === 'EventTag') {
$attribute[$tagType][$tk]['Tag']['inherited'] = 1;
}
$attribute['Tag'][] = $attribute[$tagType][$tk]['Tag'];
}
unset($attribute[$tagType]);
}
}
unset($attribute['value1']);
unset($attribute['value2']);
return json_encode($attribute);
}
private function __objectHandler($object, $options = array())
{
App::uses('JSONConverterTool', 'Tools');
return JsonTool::encode(JSONConverterTool::convertObject($object, false, true));
}
private function __attributeHandler($attribute, $options = array())
{
$attribute = array_merge($attribute['Attribute'], $attribute);
unset($attribute['Attribute']);
if (isset($attribute['Object']) && empty($attribute['Object']['id'])) {
unset($attribute['Object']);
}
$tagTypes = array('AttributeTag', 'EventTag');
foreach ($tagTypes as $tagType) {
if (isset($attribute[$tagType])) {
foreach ($attribute[$tagType] as $tag) {
if ($tagType === 'EventTag') {
$tag['Tag']['inherited'] = 1;
}
$attribute['Tag'][] = $tag['Tag'];
}
unset($attribute[$tagType]);
}
}
unset($attribute['value1']);
unset($attribute['value2']);
return JsonTool::encode($attribute);
}
private function __sightingsHandler($sighting, $options = array())
{
return json_encode($sighting);
return JsonTool::encode($sighting);
}
private function __galaxyClusterHandler($cluster, $options = array())
{
return json_encode($cluster);
return JsonTool::encode($cluster);
}
public function header($options = array())
{
if ($options['scope'] === 'Attribute') {
return '{"response": {"Attribute": [';
} else {
return '{"response": [';
}
if ($options['scope'] === 'Attribute') {
return '{"response": {"Attribute": [';
} else {
return '{"response": [';
}
}
public function footer($options = array())
{
if ($options['scope'] === 'Attribute') {
return ']}}' . PHP_EOL;
} else {
return ']}' . PHP_EOL;
}
if ($options['scope'] === 'Attribute') {
return ']}}' . PHP_EOL;
} else {
return ']}' . PHP_EOL;
}
}
public function separator()

View File

@ -1,90 +1,131 @@
<?php
class NidsExport
abstract class NidsExport
{
public $rules = array();
public $classtype = 'trojan-activity';
public $format = ""; // suricata (default), snort
public $supportedObjects = array('network-connection', 'ddos');
public $checkWhitelist = true;
public $checkWhitelist = true;
public $additional_params = array(
'contain' => array(
'Event' => array(
'fields' => array('threat_level_id')
)
),
'flatten' => 1
);
public $additional_params = array(
'contain' => array(
'Event' => array(
'fields' => array('threat_level_id')
)
),
public function handler($data, $options = array())
{
$continue = empty($format);
$this->checkWhitelist = false;
if ($options['scope'] === 'Attribute') {
$this->export(
array($data),
$options['user']['nids_sid'],
$options['returnFormat'],
$continue
);
} else if ($options['scope'] === 'Event') {
if (!empty($data['EventTag'])) {
$data['Event']['EventTag'] = $data['EventTag'];
}
if (!empty($data['Attribute'])) {
$this->__convertFromEventFormat($data['Attribute'], $data, $options, $continue);
}
if (!empty($data['Object'])) {
foreach ($data['Object'] as $object) {
$this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
}
}
}
return '';
}
);
private function __convertFromEventFormat($attributes, $event, $options = array(), $continue = false) {
$rearranged = array();
foreach ($attributes as $attribute) {
$attributeTag = array();
if (!empty($attribute['AttributeTag'])) {
$attributeTag = $attribute['AttributeTag'];
unset($attribute['AttributeTag']);
}
$rearranged[] = array(
'Attribute' => $attribute,
'AttributeTag' => $attributeTag,
'Event' => $event['Event']
);
}
$this->export(
$rearranged,
$options['user']['nids_sid'],
$options['returnFormat'],
$continue
);
return true;
public function handler($data, $options = array())
{
$continue = empty($format);
$this->checkWhitelist = false;
if ($options['scope'] === 'Attribute') {
$this->export(
array($data),
$options['user']['nids_sid'],
$options['returnFormat'],
$continue
);
} else if ($options['scope'] === 'Event') {
if (!empty($data['EventTag'])) {
$data['Event']['EventTag'] = $data['EventTag'];
}
if (!empty($data['Attribute'])) {
$this->__convertFromEventFormat($data['Attribute'], $data, $options, $continue);
}
if (!empty($data['Object'])) {
$this->__convertFromEventFormatObject($data['Object'], $data, $options, $continue);
}
}
return '';
}
}
private function __convertFromEventFormat($attributes, $event, $options = array(), $continue = false) {
public function header($options = array())
{
$this->explain();
return '';
}
$rearranged = array();
foreach ($attributes as $attribute) {
$attributeTag = array();
if (!empty($attribute['AttributeTag'])) {
$attributeTag = $attribute['AttributeTag'];
unset($attribute['AttributeTag']);
}
$rearranged[] = array(
'Attribute' => $attribute,
'AttributeTag' => $attributeTag,
'Event' => $event['Event']
);
}
$this->export(
$rearranged,
$options['user']['nids_sid'],
$options['returnFormat'],
$continue
);
return true;
public function footer()
{
return implode ("\n", $this->rules);
}
}
public function separator()
{
return '';
}
private function __convertFromEventFormatObject($objects, $event, $options = array(), $continue = false) {
$rearranged = array();
foreach ($objects as $object) {
if(in_array($object['name'], $this->supportedObjects)){
$objectTag = array();
foreach($object['Attribute'] as $attribute) {
if (!empty($attribute['AttributeTag'])) {
$objectTag = array_merge($objectTag, $attribute['AttributeTag']);
unset($attribute['AttributeTag']);
}
}
$rearranged[] = array(
'Attribute' => $object, // Using 'Attribute' instead of 'Object' to comply with function export
'AttributeTag' => $objectTag, // Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
'Event' => $event['Event']
);
} else { // In case no custom export exists for the object, the approach falls back to the attribute case
$this->__convertFromEventFormat($object['Attribute'], $event, $options, $continue);
}
}
$this->export(
$rearranged,
$options['user']['nids_sid'],
$options['returnFormat'],
$continue
);
return true;
}
public function header($options = array())
{
$this->explain();
return '';
}
public function footer()
{
return implode ("\n", $this->rules);
}
public function separator()
{
return '';
}
public function explain()
{
@ -93,7 +134,7 @@ class NidsExport
$this->rules[] = '# These NIDS rules contain some variables that need to exist in your configuration.';
$this->rules[] = '# Make sure you have set:';
$this->rules[] = '#';
$this->rules[] = '# $HOME_NET - Your internal network range';
$this->rules[] = '# $HOME_NET - Your internal network range';
$this->rules[] = '# $EXTERNAL_NET - The network considered as outside';
$this->rules[] = '# $SMTP_SERVERS - All your internal SMTP servers';
$this->rules[] = '# $HTTP_PORTS - The ports used to contain HTTP traffic (not required with suricata export)';
@ -106,10 +147,10 @@ class NidsExport
public function export($items, $startSid, $format="suricata", $continue = false)
{
$this->format = $format;
if ($this->checkWhitelist && !isset($this->Whitelist)) {
$this->Whitelist = ClassRegistry::init('Whitelist');
$this->whitelist = $this->Whitelist->getBlockedValues();
}
if ($this->checkWhitelist && !isset($this->Whitelist)) {
$this->Whitelist = ClassRegistry::init('Whitelist');
$this->whitelist = $this->Whitelist->getBlockedValues();
}
// output a short explanation
if (!$continue) {
@ -119,20 +160,20 @@ class NidsExport
foreach ($items as $item) {
// retrieve all tags for this item to add them to the msg
$tagsArray = [];
if (!empty($item['AttributeTag'])) {
foreach ($item['AttributeTag'] as $tag_attr) {
if (array_key_exists('name', $tag_attr['Tag'])) {
array_push($tagsArray, $tag_attr['Tag']['name']);
}
}
}
if (!empty($item['Event']['EventTag'])) {
foreach ($item['Event']['EventTag'] as $tag_event) {
if (array_key_exists('name', $tag_event['Tag'])) {
array_push($tagsArray, $tag_event['Tag']['name']);
}
}
}
if (!empty($item['AttributeTag'])) {
foreach ($item['AttributeTag'] as $tag_attr) {
if (array_key_exists('name', $tag_attr['Tag'])) {
array_push($tagsArray, $tag_attr['Tag']['name']);
}
}
}
if (!empty($item['Event']['EventTag'])) {
foreach ($item['Event']['EventTag'] as $tag_event) {
if (array_key_exists('name', $tag_event['Tag'])) {
array_push($tagsArray, $tag_event['Tag']['name']);
}
}
}
$ruleFormatMsgTags = implode(",", $tagsArray);
# proto src_ip src_port direction dst_ip dst_port msg rule_content tag sid rev
@ -142,69 +183,180 @@ class NidsExport
$sid = $startSid + ($item['Attribute']['id'] * 10); // leave 9 possible rules per attribute type
$sid++;
switch ($item['Attribute']['type']) {
// LATER nids - test all the snort attributes
// LATER nids - add the tag keyword in the rules to capture network traffic
// LATER nids - sanitize every $attribute['value'] to not conflict with snort
case 'ip-dst':
$this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-src':
$this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-dst|port':
$this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-src|port':
$this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email':
$this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
$this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-src':
$this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-dst':
$this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-subject':
$this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-attachment':
$this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'domain':
$this->domainRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'domain|ip':
$this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'hostname':
$this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'url':
$this->urlRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'user-agent':
$this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ja3-fingerprint-md5':
$this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
$this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'snort':
$this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
// no break
default:
break;
if(!empty($item['Attribute']['type'])) { // item is an 'Attribute'
switch ($item['Attribute']['type']) {
// LATER nids - test all the snort attributes
// LATER nids - add the tag keyword in the rules to capture network traffic
// LATER nids - sanitize every $attribute['value'] to not conflict with snort
case 'ip-dst':
$this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-src':
$this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-dst|port':
$this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ip-src|port':
$this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email':
$this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
$sid++;
$this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-src':
$this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-dst':
$this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-subject':
$this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'email-attachment':
$this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'domain':
$this->domainRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'domain|ip':
$this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'hostname':
$this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'url':
$this->urlRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'user-agent':
$this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ja3-fingerprint-md5':
$this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
$this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'snort':
$this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
// no break
default:
break;
}
} else if(!empty($item['Attribute']['name'])) { // Item is an 'Object'
switch ($item['Attribute']['name']) {
case 'network-connection':
$this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
break;
case 'ddos':
$this->ddosRule($ruleFormat, $item['Attribute'], $sid);
break;
default:
break;
}
}
}
return $this->rules;
}
public function networkConnectionRule($ruleFormat, $object, &$sid)
{
$attributes = NidsExport::getObjectAttributes($object);
if(!array_key_exists('layer4-protocol', $attributes)){
$attributes['layer4-protocol'] = 'ip'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('ip')
}
if(!array_key_exists('ip-src', $attributes)){
$attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('ip-dst', $attributes)){
$attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('src-port', $attributes)){
$attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
}
if(!array_key_exists('dst-port', $attributes)){
$attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
}
$this->rules[] = sprintf(
$ruleFormat,
false,
$attributes['layer4-protocol'], // proto
$attributes['ip-src'], // src_ip
$attributes['src-port'], // src_port
'->', // direction
$attributes['ip-dst'], // dst_ip
$attributes['dst-port'], // dst_port
'Network connection between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
);
}
public function ddosRule($ruleFormat, $object, &$sid)
{
$attributes = NidsExport::getObjectAttributes($object);
if(!array_key_exists('protocol', $attributes)){
$attributes['protocol'] = 'ip'; // If protocol is unknown, we roll-back to 'ip'
}
if(!array_key_exists('ip-src', $attributes)){
$attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('ip-dst', $attributes)){
$attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('src-port', $attributes)){
$attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
}
if(!array_key_exists('dst-port', $attributes)){
$attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
}
$this->rules[] = sprintf(
$ruleFormat,
false,
$attributes['protocol'], // proto
$attributes['ip-src'], // src_ip
$attributes['src-port'], // src_port
'->', // direction
$attributes['ip-dst'], // dst_ip
$attributes['dst-port'], // dst_port
'DDOS attack detected between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
);
}
public static function getObjectAttributes($object)
{
$attributes = array();
foreach ($object['Attribute'] as $attribute) {
$attributes[$attribute['object_relation']] = $attribute['value'];
}
return $attributes;
}
public function domainIpRule($ruleFormat, $attribute, &$sid)
{
$values = explode('|', $attribute['value']);
@ -225,17 +377,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'ip', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
$ipport[0], // dst_ip
$ipport[1], // dst_port
'Outgoing To IP: ' . $attribute['value'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
'ip', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
$ipport[0], // dst_ip
$ipport[1], // dst_port
'Outgoing To IP: ' . $attribute['value'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
);
}
@ -246,17 +398,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'ip', // proto
$ipport[0], // src_ip
$ipport[1], // src_port
'->', // direction
'$HOME_NET', // dst_ip
'any', // dst_port
'Incoming From IP: ' . $attribute['value'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
'ip', // proto
$ipport[0], // src_ip
$ipport[1], // src_port
'->', // direction
'$HOME_NET', // dst_ip
'any', // dst_port
'Incoming From IP: ' . $attribute['value'], // msg
'', // rule_content
'', // tag
$sid, // sid
1 // rev
);
}
@ -268,17 +420,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Source Email Address: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Source Email Address: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -290,17 +442,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Destination Email Address: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Destination Email Address: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -313,17 +465,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Bad Email Subject', // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Bad Email Subject', // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -336,17 +488,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Bad Email Attachment', // msg
$content, // rule_content // LATER nids - test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$EXTERNAL_NET', // src_ip
'any', // src_port
'->', // direction
'$SMTP_SERVERS', // dst_ip
'25', // dst_port
'Bad Email Attachment', // msg
$content, // rule_content // LATER nids - test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -358,33 +510,33 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Hostname: ' . $attribute['value'], // msg
$content, // rule_content
'', // tag
$sid, // sid
1 // rev
'udp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Hostname: ' . $attribute['value'], // msg
$content, // rule_content
'', // tag
$sid, // sid
1 // rev
);
$sid++;
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Hostname: ' . $attribute['value'], // msg
$content. ' flow:established;', // rule_content
'', // tag
$sid, // sid
1 // rev
'tcp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Hostname: ' . $attribute['value'], // msg
$content. ' flow:established;', // rule_content
'', // tag
$sid, // sid
1 // rev
);
$sid++;
// also do http requests
@ -392,17 +544,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP Hostname: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP Hostname: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -414,33 +566,33 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Domain: ' . $attribute['value'], // msg
$content, // rule_content
'', // tag
$sid, // sid
1 // rev
'udp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Domain: ' . $attribute['value'], // msg
$content, // rule_content
'', // tag
$sid, // sid
1 // rev
);
$sid++;
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Domain: ' . $attribute['value'], // msg
$content. ' flow:established;', // rule_content
'', // tag
$sid, // sid
1 // rev
'tcp', // proto
'any', // src_ip
'any', // src_port
'->', // direction
'any', // dst_ip
'53', // dst_port
'Domain: ' . $attribute['value'], // msg
$content. ' flow:established;', // rule_content
'', // tag
$sid, // sid
1 // rev
);
$sid++;
// also do http requests,
@ -448,17 +600,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP Domain: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP Domain: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -473,17 +625,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP URL: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing HTTP URL: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -495,17 +647,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing User-Agent: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
'tcp', // proto
'$HOME_NET', // src_ip
'any', // src_port
'->', // direction
'$EXTERNAL_NET', // dst_ip
'$HTTP_PORTS', // dst_port
'Outgoing User-Agent: ' . $attribute['value'], // msg
$content, // rule_content
'tag:session,600,seconds;', // tag
$sid, // sid
1 // rev
);
}
@ -527,37 +679,37 @@ class NidsExport
$tmpRule = str_replace(array("\r","\n"), " ", $attribute['value']);
// rebuild the rule by overwriting the different keywords using preg_replace()
// sid - '/sid\s*:\s*[0-9]+\s*;/'
// rev - '/rev\s*:\s*[0-9]+\s*;/'
// sid - '/sid\s*:\s*[0-9]+\s*;/'
// rev - '/rev\s*:\s*[0-9]+\s*;/'
// classtype - '/classtype:[a-zA-Z_-]+;/'
// msg - '/msg\s*:\s*".*?"\s*;/'
// msg - '/msg\s*:\s*".*?"\s*;/'
// reference - '/reference\s*:\s*.+?;/'
// tag - '/tag\s*:\s*.+?;/'
// tag - '/tag\s*:\s*.+?;/'
$replaceCount = array();
$tmpRule = preg_replace('/sid\s*:\s*[0-9]+\s*;/', 'sid:' . $sid . ';', $tmpRule, -1, $replaceCount['sid']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
$tmpRule = preg_replace('/rev\s*:\s*[0-9]+\s*;/', 'rev:1;', $tmpRule, -1, $replaceCount['rev']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
$tmpRule = preg_replace('/classtype:[a-zA-Z_-]+;/', 'classtype:' . $this->classtype . ';', $tmpRule, -1, $replaceCount['classtype']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
$tmpRule = preg_replace('/msg\s*:\s*"(.*?)"\s*;/', sprintf($ruleFormatMsg, 'snort-rule | $1') . ';', $tmpRule, -1, $replaceCount['msg']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
$tmpRule = preg_replace('/reference\s*:\s*.+?;/', $ruleFormatReference . ';', $tmpRule, -1, $replaceCount['reference']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
$tmpRule = preg_replace('/reference\s*:\s*.+?;/', $ruleFormatReference . ';', $tmpRule, -1, $replaceCount['reference']);
if (null == $tmpRule) {
return false;
} // don't output the rule on error with the regex
} // don't output the rule on error with the regex
// FIXME nids - implement priority overwriting
// some values were not replaced, so we need to add them ourselves, and insert them in the rule
@ -667,13 +819,13 @@ class NidsExport
public function checkWhitelist($value)
{
if ($this->checkWhitelist && is_array($this->whitelist)) {
foreach ($this->whitelist as $wlitem) {
if (preg_match($wlitem, $value)) {
return true;
}
}
}
if ($this->checkWhitelist && is_array($this->whitelist)) {
foreach ($this->whitelist as $wlitem) {
if (preg_match($wlitem, $value)) {
return true;
}
}
}
return false;
}
@ -706,15 +858,16 @@ class NidsExport
}
}
/**
* @param array $attribute
* @return array|string[]
*/
public static function getIpPort($attribute)
{
$ipport = array();
if (strpos($attribute['type'], 'port') !== false) {
$ipport = explode('|', $attribute['value']);
return explode('|', $attribute['value']);
} else {
$ipport[0] = $attribute['value'];
$ipport[1] = 'any';
return [$attribute['value'], 'any'];
}
return $ipport;
}
}

View File

@ -387,10 +387,11 @@ class AttachmentTool
* @param string $data
* @param int $maxWidth
* @param int $maxHeight
* @param string $outputFormat Can be 'png' or 'webp'
* @return string
* @throws Exception
*/
public function resizeImage($data, $maxWidth, $maxHeight)
public function resizeImage($data, $maxWidth, $maxHeight, $outputFormat = 'png')
{
$image = imagecreatefromstring($data);
if ($image === false) {
@ -425,7 +426,16 @@ class AttachmentTool
// Output image to string
ob_start();
imagepng($imageThumbnail, null, 9);
if ($outputFormat === 'webp') {
if (!function_exists('imagewebp')) {
throw new InvalidArgumentException("Webp image format is not supported.");
}
imagewebp($imageThumbnail);
} elseif ($outputFormat === 'png') {
imagepng($imageThumbnail, null, 9);
} else {
throw new InvalidArgumentException("Unsupported image format $outputFormat.");
}
$imageData = ob_get_clean();
imagedestroy($imageThumbnail);
@ -460,7 +470,8 @@ class AttachmentTool
*/
public function attachmentDirIsS3()
{
return substr(Configure::read('MISP.attachments_dir'), 0, 2) === "s3";
$attachmentsDir = Configure::read('MISP.attachments_dir');
return $attachmentsDir && substr($attachmentsDir, 0, 2) === "s3";
}
/**

View File

@ -407,7 +407,7 @@ class AttributeValidationTool
case 'dns-soa-email':
case 'jabber-id':
// we don't use the native function to prevent issues with partial email addresses
if (preg_match("#^.*\@.*\..*$#i", $value)) {
if (preg_match("#^.[^\s]*\@.*\..*$#i", $value)) {
return true;
}
return __('Email address has an invalid format. Please double check the value or select type "other".');
@ -635,11 +635,14 @@ class AttributeValidationTool
}
/**
* @param $value
* @param string $value
* @return bool
*/
private static function isSsdeep($value)
{
if (strpos($value, "\n") !== false) {
return false;
}
$parts = explode(':', $value);
if (count($parts) !== 3) {
return false;

View File

@ -88,10 +88,7 @@ class BackgroundJob implements JsonSerializable
['BACKGROUND_JOB_ID' => $this->id]
);
$this->output = stream_get_contents($pipes[1]);
$this->error = stream_get_contents($pipes[2]);
$this->returnCode = proc_close($process);
$this->pool($process, $pipes);
if ($this->returnCode === 0 && empty($stderr)) {
$this->setStatus(BackgroundJob::STATUS_COMPLETED);
@ -101,6 +98,41 @@ class BackgroundJob implements JsonSerializable
}
}
private function pool($process, array $pipes)
{
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);
$this->output = '';
$this->error = '';
while (true) {
$read = [$pipes[1], $pipes[2]];
$write = null;
$except = null;
if (false === ($changedStreams = stream_select($read, $write, $except, 5))) {
throw new RuntimeException("Could not select stream");
} elseif ($changedStreams > 0) {
$this->output .= stream_get_contents($pipes[1]);
$this->error .= stream_get_contents($pipes[2]);
}
$status = proc_get_status($process);
if (!$status['running']) {
// Just in case read rest data from stream
$this->output .= stream_get_contents($pipes[1]);
$this->error .= stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
$this->returnCode = $status['exitcode'];
break;
}
}
}
public function jsonSerialize(): array
{
return [

View File

@ -7,17 +7,17 @@ App::uses('BackgroundJob', 'Tools/BackgroundJobs');
/**
* BackgroundJobs Tool
*
*
* Utility class to queue jobs, run them and monitor workers.
*
*
* To run a worker manually (debug only):
* $ ./Console/cake start_worker [queue]
*
*
* It is recommended to run these commands with [Supervisor](http://supervisord.org).
* `Supervisor` has an extensive feature set to manage scripts as services,
* such as autorestart, parallel execution, logging, monitoring and much more.
* `Supervisor` has an extensive feature set to manage scripts as services,
* such as autorestart, parallel execution, logging, monitoring and much more.
* All can be managed via the terminal or a XML-RPC API.
*
*
* Use the following configuration as a template for the services:
* /etc/supervisor/conf.d/misp-workers.conf:
* [group:misp-workers]
@ -27,14 +27,14 @@ App::uses('BackgroundJob', 'Tools/BackgroundJobs');
* [program:default]
* command=/var/www/MISP/app/Console/cake start_worker default
* process_name=%(program_name)s_%(process_num)02d
* numprocs=5 ; adjust the amount of parallel workers to your MISP usage
* numprocs=5 ; adjust the amount of parallel workers to your MISP usage
* autostart=true
* autorestart=true
* redirect_stderr=false
* stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log
* stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log
* user=www-data
*
*
*/
class BackgroundJobsTool
{
@ -73,18 +73,21 @@ class BackgroundJobsTool
const
CMD_EVENT = 'event',
CMD_SERVER = 'server',
CMD_ADMIN = 'admin';
CMD_ADMIN = 'admin',
CMD_WORKFLOW = 'workflow';
const ALLOWED_COMMANDS = [
self::CMD_EVENT,
self::CMD_SERVER,
self::CMD_ADMIN
self::CMD_ADMIN,
self::CMD_WORKFLOW,
];
const CMD_TO_SHELL_DICT = [
self::CMD_EVENT => 'EventShell',
self::CMD_SERVER => 'ServerShell',
self::CMD_ADMIN => 'AdminShell'
self::CMD_ADMIN => 'AdminShell',
self::CMD_WORKFLOW => 'WorkflowShell',
];
const JOB_STATUS_PREFIX = 'job_status';
@ -94,7 +97,7 @@ class BackgroundJobsTool
/**
* Initialize
*
*
* Settings should have the following format:
* [
* 'enabled' => true,
@ -111,6 +114,7 @@ class BackgroundJobsTool
* ]
*
* @param array $settings
* @throws Exception
*/
public function __construct(array $settings)
{
@ -175,7 +179,7 @@ class BackgroundJobsTool
/**
* Enqueue a Job using the CakeResque.
* @deprecated
*
*
* @param string $queue Name of the queue to enqueue the job to.
* @param string $class Class of the job.
* @param array $args Arguments passed to the job.
@ -211,9 +215,9 @@ class BackgroundJobsTool
*
* @param string $queue Queue name, e.g. 'default'.
* @param int $timeout Time to block the read if the queue is empty.
* Must be less than your configured `read_write_timeout`
* Must be less than your configured `read_write_timeout`
* for the redis connection.
*
*
* @throws Exception
*/
public function dequeue($queue, int $timeout = 30)
@ -233,8 +237,6 @@ class BackgroundJobsTool
* Get the job status.
*
* @param string $jobId Background Job Id.
*
*
*/
public function getJob(string $jobId)
{
@ -263,7 +265,7 @@ class BackgroundJobsTool
* Clear all the queue's jobs.
*
* @param string $queue Queue name, e.g. 'default'.
*
*
* @return boolean True on success, false on failure.
*/
public function clearQueue($queue): bool
@ -310,7 +312,7 @@ class BackgroundJobsTool
* Get the number of jobs inside a queue.
*
* @param string $queue Queue name, e.g. 'default'.
*
*
* @return integer Number of jobs.
*/
public function getQueueSize(string $queue): int
@ -328,7 +330,7 @@ class BackgroundJobsTool
* Update job
*
* @param BackgroundJob $job
*
*
* @return void
*/
public function update(BackgroundJob $job)
@ -366,9 +368,10 @@ class BackgroundJobsTool
/**
* Start worker by queue
*
* @param string $name
* @param string $queue Queue name
* @param boolean $waitForRestart
* @return boolean
* @throws Exception
*/
public function startWorkerByQueue(string $queue, bool $waitForRestart = false): bool
{
@ -401,6 +404,7 @@ class BackgroundJobsTool
* @param string|int $id
* @param boolean $waitForRestart
* @return boolean
* @throws Exception
*/
public function stopWorker($id, bool $waitForRestart = false): bool
{
@ -428,6 +432,7 @@ class BackgroundJobsTool
*
* @param boolean $waitForRestart
* @return void
* @throws Exception
*/
public function restartWorkers(bool $waitForRestart = false)
{
@ -440,6 +445,7 @@ class BackgroundJobsTool
*
* @param boolean $waitForRestart
* @return void
* @throws Exception
*/
public function restartDeadWorkers(bool $waitForRestart = false)
{
@ -499,6 +505,7 @@ class BackgroundJobsTool
* Return true if Supervisor process is running.
*
* @return boolean
* @throws Exception
*/
public function getSupervisorStatus(): bool
{
@ -508,8 +515,8 @@ class BackgroundJobsTool
/**
* Validate queue
*
* @param string $queue
* @return boolean
* @throws InvalidArgumentException
*/
private function validateQueue(string $queue): bool
{
@ -529,8 +536,8 @@ class BackgroundJobsTool
/**
* Validate command
*
* @param string $command
* @return boolean
* @throws InvalidArgumentException
*/
private function validateCommand(string $command): bool
{
@ -569,13 +576,21 @@ class BackgroundJobsTool
/**
* @return Redis
* @throws Exception
*/
private function createRedisConnection(): Redis
{
if (!class_exists('Redis')) {
throw new Exception("Class Redis doesn't exists. Please install redis extension for PHP.");
}
$redis = new Redis();
$redis->connect($this->settings['redis_host'], $this->settings['redis_port']);
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
$redis->setOption(Redis::OPT_PREFIX, $this->settings['redis_namespace'] . ':');
if (isset($this->settings['redis_read_timeout'])) {
$redis->setOption(Redis::OPT_READ_TIMEOUT, $this->settings['redis_read_timeout']);
}
$redisPassword = $this->settings['redis_password'];
if (!empty($redisPassword)) {
@ -588,6 +603,7 @@ class BackgroundJobsTool
/**
* @return \Supervisor\Supervisor
* @throws Exception
*/
private function getSupervisor()
{
@ -613,6 +629,10 @@ class BackgroundJobsTool
];
}
if (!isset($this->settings['supervisor_host'])) {
throw new RuntimeException("Required option `supervisor_host` for BackgroundJobsTool is not set.");
}
$host = null;
if (substr($this->settings['supervisor_host'], 0, 5) === 'unix:') {
if (!defined('CURLOPT_UNIX_SOCKET_PATH')) {

View File

@ -0,0 +1,63 @@
<?php
class BetterCakeEventManager extends CakeEventManager
{
/**
* This method is similar as original dispatch, but do not return newly created event. With returning event, there is
* big memory leak in PHP at least for PHP version 7.4.19.
* @param CakeEvent $event
*/
public function dispatch($event)
{
$listeners = $this->listeners($event->name());
if (empty($listeners)) {
return null;
}
foreach ($listeners as $listener) {
if ($event->isStopped()) {
break;
}
if ($listener['passParams'] === true) {
$result = call_user_func_array($listener['callable'], $event->data);
} else {
$result = $listener['callable']($event);
}
if ($result === false) {
$event->stopPropagation();
}
if ($result !== null) {
$event->result = $result;
}
}
}
/**
* @param $eventKey
* @return array
*/
public function listeners($eventKey)
{
if ($this->_isGlobal) {
$localListeners = [];
} else {
$localListeners = $this->_listeners[$eventKey] ?? [];
}
$globalListeners = static::instance()->prioritisedListeners($eventKey);
$priorities = array_merge(array_keys($globalListeners), array_keys($localListeners));
$priorities = array_unique($priorities, SORT_REGULAR);
asort($priorities);
$result = [];
foreach ($priorities as $priority) {
if (isset($globalListeners[$priority])) {
$result = array_merge($result, $globalListeners[$priority]);
}
if (isset($localListeners[$priority])) {
$result = array_merge($result, $localListeners[$priority]);
}
}
return $result;
}
}

View File

@ -78,9 +78,15 @@ class CidrTool
return false;
}
$maximumNetmask = strlen($ipBytes) === 4 ? 32 : 128;
if (isset($parts[1]) && ($parts[1] > $maximumNetmask || $parts[1] < 0)) {
return false; // Netmask part of CIDR is invalid
if (isset($parts[1])) {
if (!ctype_digit($parts[1])) {
return false;
}
$maximumNetmask = strlen($ipBytes) === 4 ? 32 : 128;
if ($parts[1] > $maximumNetmask || $parts[1] < 0) {
return false; // Netmask part of CIDR is invalid
}
}
return true;

View File

@ -1,8 +1,10 @@
<?php
class ColourPaletteTool
{
// pass the number of distinct colours to receive an array of colours
/**
* @param int $count Pass the number of distinct colours to receive an array of colours
* @return array
*/
public function createColourPalette($count)
{
$interval = 1 / $count;
@ -13,6 +15,10 @@ class ColourPaletteTool
return $colours;
}
/**
* @param array $hsv
* @return string
*/
public function HSVtoRGB(array $hsv)
{
list($H, $S, $V) = $hsv;
@ -50,12 +56,16 @@ class ColourPaletteTool
return $this->convertToHex(array($R, $G, $B));
}
/**
* @param array $channels
* @return string
*/
public function convertToHex($channels)
{
$colour = '#';
foreach ($channels as $channel) {
$channel = strval(dechex(round($channel*255)));
if (strlen($channel) == 1) {
$channel = dechex(round($channel*255));
if (strlen($channel) === 1) {
$channel = '0' . $channel;
}
$colour .= $channel;

View File

@ -31,17 +31,14 @@ class ComplexTypeTool
)
);
const HEX_HASH_TYPES = array(
32 => array('single' => array('md5', 'imphash', 'x509-fingerprint-md5'), 'composite' => array('filename|md5', 'filename|imphash')),
40 => array('single' => array('sha1', 'pehash', 'x509-fingerprint-sha1', 'cdhash'), 'composite' => array('filename|sha1', 'filename|pehash')),
56 => array('single' => array('sha224', 'sha512/224'), 'composite' => array('filename|sha224', 'filename|sha512/224')),
64 => array('single' => array('sha256', 'authentihash', 'sha512/256', 'x509-fingerprint-sha256'), 'composite' => array('filename|sha256', 'filename|authentihash', 'filename|sha512/256')),
96 => array('single' => array('sha384'), 'composite' => array('filename|sha384')),
128 => array('single' => array('sha512'), 'composite' => array('filename|sha512'))
);
// algorithms to run through in order, without Hashes that are checked separately
const CHECKS = array('Email', 'IP', 'DomainOrFilename', 'SimpleRegex', 'AS', 'BTC');
const HEX_HASH_TYPES = [
32 => ['single' => ['md5', 'imphash', 'x509-fingerprint-md5', 'ja3-fingerprint-md5'], 'composite' => ['filename|md5', 'filename|imphash']],
40 => ['single' => ['sha1', 'pehash', 'x509-fingerprint-sha1', 'cdhash'], 'composite' => ['filename|sha1', 'filename|pehash']],
56 => ['single' => ['sha224', 'sha512/224'], 'composite' => ['filename|sha224', 'filename|sha512/224']],
64 => ['single' => ['sha256', 'authentihash', 'sha512/256', 'x509-fingerprint-sha256'], 'composite' => ['filename|sha256', 'filename|authentihash', 'filename|sha512/256']],
96 => ['single' => ['sha384'], 'composite' => ['filename|sha384']],
128 => ['single' => ['sha512'], 'composite' => ['filename|sha512']],
];
private $__tlds = null;
@ -179,6 +176,9 @@ class ComplexTypeTool
continue;
}
foreach ($row as $elementPos => $element) {
if (empty($element)) {
continue;
}
if (empty($values) || in_array(($elementPos + 1), $values)) {
$element = trim($element, " \t\n\r\0\x0B\"\'");
if (empty($element)) {
@ -198,23 +198,28 @@ class ComplexTypeTool
return $iocArray;
}
public function checkFreeText($input, $settings = array())
/**
* @param string $input
* @param array $settings
* @return array
*/
public function checkFreeText($input, array $settings = [])
{
$charactersToTrim = '\'".,() ' . "\t\n\r\0\x0B"; // custom + default PHP trim
$input = str_replace("\xc2\xa0", ' ', $input); // non breaking space to normal space
$input = preg_replace('/\p{C}+/u', ' ', $input);
$iocArray = preg_split("/\r\n|\n|\r|\s|\s+|,|\<|\>|;/", $input);
preg_match_all('/\"([^\"]*)\"/', $input, $matches);
foreach ($matches[1] as $match) {
if ($match !== '') {
$iocArray[] = $match;
}
}
preg_match_all('/\"([^\"]*)\"/', $input, $matches);
foreach ($matches[1] as $match) {
if ($match !== '') {
$iocArray[] = $match;
}
}
unset($matches);
$resultArray = [];
foreach ($iocArray as $ioc) {
$ioc = trim($ioc, $charactersToTrim);
$ioc = trim($ioc, '\'".,() ' . "\t\n\r\0\x0B"); // custom + default PHP trim
if (empty($ioc)) {
continue;
}
@ -251,23 +256,37 @@ class ComplexTypeTool
];
}
$input = array('raw' => $raw_input);
$input = ['raw' => $raw_input];
// Check hashes before refang and port extracting, it is not necessary for hashes. This speedups parsing
// freetexts or CSVs with a lot of hashes.
$hashes = $this->__checkForHashes($input);
if ($hashes) {
return $hashes;
if ($result = $this->__checkForHashes($input)) {
return $result;
}
$input = $this->__refangInput($input);
$input = $this->__extractPort($input);
foreach (self::CHECKS as $check) {
$result = $this->{'__checkFor' . $check}($input);
if ($result) {
return $result;
}
// Check email before port extracting, it is not necessary for email. This speedups parsing
// freetexts or CSVs with a lot of emails.
if ($result = $this->__checkForEmail($input)) {
return $result;
}
$input = $this->__extractPort($input);
if ($result = $this->__checkForIP($input)) {
return $result;
}
if ($result = $this->__checkForDomainOrFilename($input)) {
return $result;
}
if ($result = $this->__checkForSimpleRegex($input)) {
return $result;
}
if ($result = $this->__checkForAS($input)) {
return $result;
}
if ($result = $this->__checkForBTC($input)) {
return $result;
}
return false;
}
@ -290,7 +309,12 @@ class ComplexTypeTool
// quick filter for an @ to see if we should validate a potential e-mail address
if (strpos($input['refanged'], '@') !== false) {
if (filter_var($input['refanged'], FILTER_VALIDATE_EMAIL)) {
return array('types' => array('email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email'), 'to_ids' => true, 'default_type' => 'email-src', 'value' => $input['refanged']);
return [
'types' => array('email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email'),
'to_ids' => true,
'default_type' => 'email-src',
'value' => $input['refanged'],
];
}
}
return false;
@ -356,17 +380,17 @@ class ComplexTypeTool
private function __refangInput($input)
{
$input['refanged'] = $input['raw'];
$refanged = $input['raw'];
foreach (self::REFANG_REGEX_TABLE as $regex) {
$input['refanged'] = preg_replace($regex['from'], $regex['to'], $input['refanged']);
$refanged = preg_replace($regex['from'], $regex['to'], $refanged);
}
$input['refanged'] = rtrim($input['refanged'], ".");
$refanged = rtrim($refanged, ".");
$input['refanged'] = preg_replace_callback(
'/\[.\]/',
function ($matches) {
return trim($matches[0], '[]');
},
$input['refanged']
$refanged
);
return $input;
}

View File

@ -173,4 +173,21 @@ class CryptGpgExtended extends Crypt_GPG
return $armored;
}
/**
* @param mixed $data
* @param bool $isFile
* @param bool $allowEmpty
* @return resource|string|null
* @throws Crypt_GPG_FileException
* @throws Crypt_GPG_NoDataException
*/
protected function _prepareInput($data, $isFile = false, $allowEmpty = true)
{
if ($isFile && $data instanceof TmpFileTool) {
return $data->resource();
}
return parent::_prepareInput($data, $isFile, $allowEmpty);
}
}

View File

@ -83,14 +83,14 @@ class CustomPaginationTool
$items = array_values($items);
}
public function sortArray($items, $params, $escapeReindex = false)
public function sortArray(array $items, $params, $escapeReindex = false)
{
if (isset($params['sort'])) {
$sortArray = array();
foreach ($items as $k => $item) {
$sortArray[$k] = !empty(Hash::get($item, $params['sort'])) ? $item[$params['sort']] : '';
$sortArray[$k] = !empty($item[$params['sort']]) ? $item[$params['sort']] : '';
}
if (empty($params['options']['direction']) || $params['options']['direction'] == 'asc') {
if (empty($params['options']['direction']) || $params['options']['direction'] === 'asc') {
asort($sortArray);
} else {
arsort($sortArray);
@ -107,12 +107,13 @@ class CustomPaginationTool
return $items;
}
public function applyRulesOnArray(&$items, $options, $model, $sort = 'id', $focusKey = 'uuid', $escapeReindex = false)
public function applyRulesOnArray(array &$items, $options, $model, $sort = 'id', $focusKey = 'uuid', $escapeReindex = false)
{
$params = $this->createPaginationRules($items, $options, $model, $sort, $focusKey);
$items = $this->sortArray($items, $params, $escapeReindex);
if (!empty($params['options']['focus'])) {
$focus = $params['options']['focus'];
$focus = $params['options']['focus'];
foreach ($items as $k => $item) {
if ($item[$focusKey] === $focus) {
$params['page'] = 1 + intval(floor($k / $params['limit']));
@ -122,6 +123,7 @@ class CustomPaginationTool
}
unset($params['options']['focus']);
}
// Start array from one
array_unshift($items, 'dummy');
unset($items[0]);
$this->truncateByPagination($items, $params);

View File

@ -96,7 +96,7 @@ class FileAccessTool
* @param bool $createFolder
* @throws Exception
*/
public static function writeToFile($file, $content, $createFolder = false)
public static function writeToFile($file, $content, $createFolder = false, $append = false)
{
$dir = dirname($file);
if ($createFolder && !is_dir($dir)) {
@ -105,7 +105,7 @@ class FileAccessTool
}
}
if (file_put_contents($file, $content, LOCK_EX) === false) {
if (file_put_contents($file, $content, LOCK_EX | (!empty($append) ? FILE_APPEND : 0)) === false) {
$freeSpace = disk_free_space($dir);
throw new Exception("An error has occurred while attempt to write to file `$file`. Maybe not enough space? ($freeSpace bytes left)");
}
@ -168,4 +168,36 @@ class FileAccessTool
return true;
}
}
/**
* @param array $submittedFile
* @param string $alternate
* @return string
*/
public static function getTempUploadedFile($submittedFile, $alternate = false)
{
if ($submittedFile['name'] != '' && $alternate != '') {
throw new MethodNotAllowedException(__('Only one import field can be used'));
}
if ($submittedFile['size'] > 0) {
$filename = basename($submittedFile['name']);
if (!is_uploaded_file($submittedFile['tmp_name'])) {
throw new InternalErrorException(__('PHP says file was not uploaded. Are you attacking me?'));
}
$file = new File($submittedFile['tmp_name']);
$file_content = $file->read();
$file->close();
if ((isset($submittedFile['error']) && $submittedFile['error'] == 0) ||
(!empty($submittedFile['tmp_name']) && $submittedFile['tmp_name'] != '')
) {
if (!$file_content) {
throw new InternalErrorException(__('PHP says file was not uploaded. Are you attacking me?'));
}
}
$text = $file_content;
} else {
$text = $alternate ? $alternate : '';
}
return $text;
}
}

View File

@ -1,6 +1,9 @@
<?php
class GpgTool
{
/** @var CryptGpgExtended */
private $gpg;
/**
* @return CryptGpgExtended
* @throws Exception
@ -22,19 +25,16 @@ class GpgTool
throw new Exception("Configuration option 'GnuPG.homedir' is not set, Crypt_GPG cannot be initialized.");
}
$options = array(
$options = [
'homedir' => $homedir,
'gpgconf' => Configure::read('GnuPG.gpgconf'),
'binary' => Configure::read('GnuPG.binary') ?: '/usr/bin/gpg',
);
];
return new CryptGpgExtended($options);
}
/** @var CryptGpgExtended */
private $gpg;
public function __construct($gpg)
public function __construct(CryptGpgExtended $gpg = null)
{
$this->gpg = $gpg;
}
@ -47,11 +47,13 @@ class GpgTool
public function searchGpgKey($search)
{
$uri = 'https://openpgp.circl.lu/pks/lookup?search=' . urlencode($search) . '&op=index&fingerprint=on&options=mr';
$response = $this->keyServerLookup($uri);
if ($response->code == 404) {
return array(); // no keys found
} else if ($response->code != 200) {
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($uri);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
return [];
}
throw $e;
}
return $this->extractKeySearch($response->body);
}
@ -64,11 +66,13 @@ class GpgTool
public function fetchGpgKey($fingerprint)
{
$uri = 'https://openpgp.circl.lu/pks/lookup?search=0x' . urlencode($fingerprint) . '&op=get&options=mr';
$response = $this->keyServerLookup($uri);
if ($response->code == 404) {
return null; // key with given fingerprint not found
} else if ($response->code != 200) {
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($uri);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
return null;
}
throw $e;
}
$key = $response->body;
@ -102,6 +106,7 @@ class GpgTool
if (empty($primaryKey)) {
throw new Exception("No primary key found");
}
$this->gpg->importKey($keyData);
return $primaryKey->getFingerprint();
}
@ -168,30 +173,20 @@ class GpgTool
$advancedUrl = "https://openpgpkey.$domain/.well-known/openpgpkey/" . strtolower($domain) . "/hu/$localPartHash";
try {
$response = $this->keyServerLookup($advancedUrl);
return $this->processWkdResponse($response);
return $this->gpg->enarmor($response->body());
} catch (Exception $e) {
// pass, continue to direct method
}
$directUrl = "https://$domain/.well-known/openpgpkey/hu/$localPartHash";
$response = $this->keyServerLookup($directUrl);
return $this->processWkdResponse($response);
}
/**
* @param HttpSocketResponse $response
* @return string
* @throws Crypt_GPG_Exception
* @throws Crypt_GPG_InvalidOperationException
*/
private function processWkdResponse(HttpSocketResponse $response)
{
if ($response->code == 404) {
throw new NotFoundException("Key not found");
} else if (!$response->isOk()) {
throw new Exception("Fetching the WKD failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($directUrl);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
throw new NotFoundException("Key not found");
}
throw $e;
}
return $this->gpg->enarmor($response->body());
}
@ -231,17 +226,18 @@ class GpgTool
/**
* @param string $uri
* @return HttpSocketResponse
* @return HttpSocketResponseExtended
* @throws HttpSocketHttpException
* @throws Exception
*/
private function keyServerLookup($uri)
{
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket();
$HttpSocket = $syncTool->createHttpSocket(['compress' => true]);
$response = $HttpSocket->get($uri);
if ($response === false) {
throw new Exception("Could not fetch '$uri'.");
if (!$response->isOk()) {
throw new HttpSocketHttpException($response, $uri);
}
return $response;
}

View File

@ -0,0 +1,97 @@
<?php
class GraphvizDOTTool
{
const NODE_STYLE = [
'trigger' => [
'margin' => 0,
'shape' => 'diamond',
],
'logic' => [
'margin' => 0,
'shape' => 'parallelogram',
],
'action' => [
'margin' => 0,
'shape' => 'box',
],
];
const EDGE_STYLE = [
];
/**
* dot Get DOT language format of the provided graph
*
* @return string
*/
public static function dot(array $graph_data)
{
$parsedGraph = self::__parseGraph($graph_data);
$str = self::__header();
$str .= self::__nodes($parsedGraph['nodes']);
$str .= self::__edges($parsedGraph['edges']);
$str .= self::__footer();
return $str;
}
private static function __parseGraph($graph_data)
{
$graphUtil = new GraphUtil($graph_data);
$nodes = $graphUtil->graph;
$edges = $graphUtil->edgeList;
return [
'nodes' => $nodes,
'edges' => $edges,
];
}
private static function __header()
{
return 'digraph G {' . PHP_EOL;
}
private static function __footer()
{
return '}';
}
private static function __nodes($nodes)
{
$str = ' {' . PHP_EOL;
foreach ($nodes as $node) {
$str .= ' ' . self::__node($node);
}
$str .= ' }' . PHP_EOL;
return $str;
}
private static function __node(array $node)
{
$node_attributes = self::NODE_STYLE[$node['data']['module_type']];
$node_attributes['label'] = $node['data']['name'];
$node_attributes_text = self::__arrayToAttributes($node_attributes);
return sprintf('%s [%s]' . PHP_EOL, $node['id'], $node_attributes_text);
}
private static function __edges($edges)
{
$str = '';
foreach ($edges as $source_id => $target_ids) {
foreach ($target_ids as $target_id) {
$str .= ' ' . self::__edge($source_id, $target_id);
}
}
return $str;
}
private static function __edge($source_id, $target_id)
{
return sprintf('%s -> %s [%s]' . PHP_EOL, $source_id, $target_id, self::__arrayToAttributes(self::EDGE_STYLE));
}
private static function __arrayToAttributes(array $list)
{
return implode(', ', array_map(function ($key, $value) {
return sprintf('%s="%s"', $key, $value);
}, array_keys($list), $list));
}
}

View File

@ -65,6 +65,14 @@ class HttpSocketJsonException extends Exception
class HttpSocketResponseExtended extends HttpSocketResponse
{
/**
* @return bool
*/
public function isNotModified()
{
return $this->code == 304;
}
/**
* @param string $message
* @throws SocketException

View File

@ -21,7 +21,7 @@ class JSONConverterTool
public static function convertObject($object, $isSiteAdmin = false, $raw = false)
{
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event');
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event', 'CryptographicKey');
foreach ($toRearrange as $element) {
if (isset($object[$element])) {
$object['Object'][$element] = $object[$element];
@ -40,7 +40,7 @@ class JSONConverterTool
public static function convert($event, $isSiteAdmin=false, $raw = false)
{
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport');
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport', 'CryptographicKey');
foreach ($toRearrange as $object) {
if (isset($event[$object])) {
$event['Event'][$object] = $event[$object];
@ -93,7 +93,7 @@ class JSONConverterTool
if ($raw) {
return $result;
}
return json_encode($result, JSON_PRETTY_PRINT);
return json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
/**
@ -112,7 +112,6 @@ class JSONConverterTool
yield json_encode($event, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return;
}
yield '{"Event":{';
$firstKey = key($event['Event']);
foreach ($event['Event'] as $key => $value) {
@ -128,7 +127,7 @@ class JSONConverterTool
}
}
if (isset($event['errors'])) {
yield '},"errors":' . json_encode($event['errors']) . '}';
yield '},"errors":' . json_encode($event['errors'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '}';
} else {
yield "}}";
}
@ -188,11 +187,7 @@ class JSONConverterTool
$resultArray = ': ' . $array . PHP_EOL;
}
if ($root) {
$text = '';
foreach ($resultArray as $r) {
$text .= $r;
}
return $text;
return implode('', $resultArray);
} else {
return $resultArray;
}

Some files were not shown because too many files have changed in this diff Show More