mirror of https://github.com/MISP/MISP
Merge branch '2.4' of https://github.com/MISP/MISP into rework_stix
commit
5e83761f83
|
@ -2,7 +2,7 @@
|
|||
|
||||
* One Pull Request per fix/feature/change/...
|
||||
* Keep the amount of commits per PR as small as possible: if for any reason, you need to fix your commit after the pull request, please squash the changes in one single commit (or tell us why not)
|
||||
* Always make sure it is mergeable in the default branch (as of today 2016-06-03: branch 2.4)
|
||||
* Always make sure it is mergeable in the default branch (as of today 2020-05-05: branch 2.4)
|
||||
* Please make sure Travis CI works on this request, or update the test cases if needed
|
||||
* Any major changes adding a functionality should be disabled by default in the config
|
||||
|
||||
|
@ -16,8 +16,3 @@ If it fixes an existing issue, please use github syntax: `#<IssueID>`
|
|||
- [ ] Does it require a DB change?
|
||||
- [ ] Are you using it in production?
|
||||
- [ ] Does it require a change in the API (PyMISP for example)?
|
||||
|
||||
#### Release Type:
|
||||
- [ ] Major
|
||||
- [ ] Minor
|
||||
- [X] Patch
|
||||
|
|
|
@ -377,18 +377,19 @@ EOF
|
|||
checkInstaller () {
|
||||
# Workaround: shasum is not available on RHEL, only checking sha512
|
||||
if [[ $FLAVOUR == "rhel" ]] || [[ $FLAVOUR == "centos" ]]; then
|
||||
INSTsum=$(sha512sum ${0} | cut -f1 -d\ )
|
||||
/usr/bin/wget --no-cache -q -O /tmp/INSTALL.sh.sha512 https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh.sha512
|
||||
INSTsum=$(sha512sum ${0} | cut -f1 -d\ )
|
||||
/usr/bin/wget --no-cache -q -O /tmp/INSTALL.sh.sha512 https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh.sha512
|
||||
chsum=$(cat /tmp/INSTALL.sh.sha512)
|
||||
if [[ "${chsum}" == "${INSTsum}" ]]; then
|
||||
echo "SHA512 matches"
|
||||
else
|
||||
echo "SHA512: ${chsum} does not match the installer sum of: ${INSTsum}"
|
||||
# exit 1 # uncomment when/if PR is merged
|
||||
fi
|
||||
if [[ "${chsum}" == "${INSTsum}" ]]; then
|
||||
echo "SHA512 matches"
|
||||
else
|
||||
echo "SHA512: ${chsum} does not match the installer sum of: ${INSTsum}"
|
||||
# exit 1 # uncomment when/if PR is merged
|
||||
fi
|
||||
else
|
||||
# TODO: Implement $FLAVOUR checks and install depending on the platform we are on
|
||||
if [[ $(which shasum > /dev/null 2>&1 ; echo $?) != 0 ]]; then
|
||||
sudo apt update
|
||||
sudo apt install libdigest-sha-perl -qyy
|
||||
fi
|
||||
# SHAsums to be computed, not the -- notatiation is for ease of use with rhash
|
||||
|
@ -672,8 +673,7 @@ setBaseURL () {
|
|||
CONN=$(ip -br -o -4 a |grep UP |head -1 |tr -d "UP")
|
||||
IFACE=`echo $CONN |awk {'print $1'}`
|
||||
IP=`echo $CONN |awk {'print $2'}| cut -f1 -d/`
|
||||
# TODO: Consider "QEMU"
|
||||
if [[ "$(checkManufacturer)" != "innotek GmbH" ]] && [[ "$(checkManufacturer)" != "VMware, Inc." ]]; then
|
||||
if [[ "$(checkManufacturer)" != "innotek GmbH" ]] && [[ "$(checkManufacturer)" != "VMware, Inc." ]] && [[ "$(checkManufacturer)" != "QEMU" ]]; then
|
||||
debug "We guess that this is a physical machine and cannot possibly guess what the MISP_BASEURL might be."
|
||||
if [[ "$UNATTENDED" != "1" ]]; then
|
||||
echo "You can now enter your own MISP_BASEURL, if you wish to NOT do that, the MISP_BASEURL will be empty, which will work, but ideally you configure it afterwards."
|
||||
|
@ -701,12 +701,20 @@ setBaseURL () {
|
|||
MISP_BASEURL="https://misp.local"
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
else
|
||||
elif [[ "$(checkManufacturer)" == "innotek GmbH" ]]; then
|
||||
MISP_BASEURL='https://localhost:8443'
|
||||
IP=$(ip addr show | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}' |grep -v "127.0.0.1" |tail -1)
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 8443 -j DNAT --to ${IP}:443
|
||||
# Webserver configuration
|
||||
FQDN='localhost.localdomain'
|
||||
elif [[ "$(checkManufacturer)" == "VMware, Inc." ]]; then
|
||||
MISP_BASEURL='""'
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
else
|
||||
MISP_BASEURL='""'
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1651,10 +1659,27 @@ WantedBy=multi-user.target" | sudo tee /etc/systemd/system/misp-workers.service
|
|||
# Main MISP Modules install function
|
||||
mispmodules () {
|
||||
cd /usr/local/src/
|
||||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
## TODO: checkUsrLocalSrc in main doc
|
||||
debug "Cloning misp-modules"
|
||||
$SUDO_CMD git clone https://github.com/MISP/misp-modules.git
|
||||
cd misp-modules
|
||||
$SUDO_CMD git clone git://github.com/stricaud/gtcaca.git
|
||||
$SUDO_CMD git clone git://github.com/stricaud/faup.git
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
|
||||
# Install gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
cd build
|
||||
$SUDO_CMD cmake .. && $SUDO_CMD make
|
||||
sudo make install
|
||||
cd ../../faup
|
||||
# Install faup
|
||||
$SUDO_CMD mkdir -p build
|
||||
cd build
|
||||
$SUDO_CMD cmake .. && $SUDO_CMD make
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ../../misp-modules
|
||||
# some misp-modules dependencies
|
||||
sudo apt install libpq5 libjpeg-dev tesseract-ocr libpoppler-cpp-dev imagemagick libopencv-dev zbar-tools libzbar0 libzbar-dev libfuzzy-dev -y
|
||||
# If you build an egg, the user you build it as need write permissions in the CWD
|
||||
|
@ -1809,8 +1834,8 @@ mail2misp () {
|
|||
cd /usr/local/src/
|
||||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
$SUDO_CMD git clone https://github.com/MISP/mail_to_misp.git
|
||||
$SUDO_CMD git clone git://github.com/stricaud/faup.git faup
|
||||
$SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca
|
||||
[[ ! -d "faup" ]] && $SUDO_CMD git clone git://github.com/stricaud/faup.git faup
|
||||
[[ ! -d "gtcaca" ]] && $SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup mail_to_misp gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
|
@ -2651,21 +2676,21 @@ installSupported () {
|
|||
|
||||
if [[ "$1" =~ ^PHP= ]]; then
|
||||
PHP_VER=$(echo $1 |cut -f2 -d=)
|
||||
if [[ "$PHP_VER" == "7.2" ]]; then
|
||||
if [[ "$PHP_VER" == 7.2 ]]; then
|
||||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
elif [[ "$PHP_VER" == "7.3" ]]; then
|
||||
elif [[ "$PHP_VER" == 7.3 ]]; then
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp74
|
||||
elif [[ "$PHP_VER" == "7.4" ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp73
|
||||
elif [[ "$PHP_VER" == "7.0" ]]; then
|
||||
elif [[ "$PHP_VER" == 7.4 ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp74
|
||||
elif [[ "$PHP_VER" == 7.0 ]]; then
|
||||
# Install PHP 7.0 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp70
|
||||
fi
|
||||
else
|
||||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
# Install PHP 7.2 Dependencies by dangerous default - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
fi
|
||||
progress 4
|
||||
|
@ -3173,6 +3198,7 @@ x86_64-fedora-30
|
|||
x86_64-debian-stretch
|
||||
x86_64-debian-buster
|
||||
x86_64-ubuntu-bionic
|
||||
x86_64-ubuntu-focal
|
||||
x86_64-kali-2019.1
|
||||
x86_64-kali-2019.2
|
||||
x86_64-kali-2019.3
|
||||
|
@ -3187,6 +3213,7 @@ armv7l-debian-jessie
|
|||
armv7l-debian-stretch
|
||||
armv7l-debian-buster
|
||||
armv7l-ubuntu-bionic
|
||||
armv7l-ubuntu-focal
|
||||
"
|
||||
|
||||
# Check if we actually support this configuration
|
||||
|
@ -3208,12 +3235,18 @@ if [ "${FLAVOUR}" == "ubuntu" ]; then
|
|||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
fi
|
||||
if [ "${RELEASE}" == "20.04" ]; then
|
||||
echo "Install on Ubuntu 20.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
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"
|
||||
installSupported && exit || exit
|
||||
fi
|
||||
if [ "${RELEASE}" == "19.04" ]; then
|
||||
echo "Install on Ubuntu 19.04 under development."
|
||||
echo "Install on Ubuntu 19.04 partially supported bye."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
exit 1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; Generated by RHash v1.3.8 on 2020-04-27 at 19:39.56
|
||||
; Generated by RHash v1.3.8 on 2020-05-01 at 12:24.54
|
||||
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
|
||||
;
|
||||
; 130366 19:39.56 2020-04-27 INSTALL.sh
|
||||
INSTALL.sh 8AFDDFA23C1154790947FA1C09DBC7599614F48D 1DC92AFF146065ECB85D5C5C211E252D5C54D6F86A61C29DDED37A9ECA4540E4 3F2C936AEE2773A29DD02477E463402945576DEC32C3003BFF3132B783E2C93D14EA99E8FADFEEE655C746D4C634BE08 DBBF519D97372DDD5337C1F58252BFE9A86B0C8ABB95A375420D8D543C5239D71C24287A75FAFC389DBFDC657B1B4080530D0D3CB44D5F94152F2ABBB9B23174
|
||||
; 131599 12:24.54 2020-05-01 INSTALL.sh
|
||||
INSTALL.sh 459A7BDAD0014A5BC5BDC4FBB2AEA8A18958D170 EE218F9A83C4E22A6DA65780FB5EB6624104C71A9B3D1743EDB4511AB790B57B B7D0734C17AC66C6B714DCF24CA6053DB31EC635E4AFD2F741601DF2413B1798A9FC9295A8DDE443F315C226DEB9E5A5 D28781C84601334CC88F1C57A869487FAB2CFD199CE83AE658798F3A75777741A49B561C72E85BB9537556E5D3785153339FBD9EEB396088BD0A2DEFE5199319
|
||||
|
|
|
@ -1 +1 @@
|
|||
8afddfa23c1154790947fa1c09dbc7599614f48d INSTALL.sh
|
||||
459a7bdad0014a5bc5bdc4fbb2aea8a18958d170 INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
1dc92aff146065ecb85d5c5c211e252d5c54d6f86a61c29dded37a9eca4540e4 INSTALL.sh
|
||||
ee218f9a83c4e22a6da65780fb5eb6624104c71a9b3d1743edb4511ab790b57b INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
3f2c936aee2773a29dd02477e463402945576dec32c3003bff3132b783e2c93d14ea99e8fadfeee655c746d4c634be08 INSTALL.sh
|
||||
b7d0734c17ac66c6b714dcf24ca6053db31ec635e4afd2f741601df2413b1798a9fc9295a8dde443f315c226deb9e5a5 INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
dbbf519d97372ddd5337c1f58252bfe9a86b0c8abb95a375420d8d543c5239d71c24287a75fafc389dbfdc657b1b4080530d0d3cb44d5f94152f2abbb9b23174 INSTALL.sh
|
||||
d28781c84601334cc88f1c57a869487fab2cfd199ce83ae658798f3a75777741a49b561c72e85bb9537556e5d3785153339fbd9eeb396088bd0a2defe5199319 INSTALL.sh
|
||||
|
|
|
@ -278,21 +278,21 @@ installSupported () {
|
|||
|
||||
if [[ "$1" =~ ^PHP= ]]; then
|
||||
PHP_VER=$(echo $1 |cut -f2 -d=)
|
||||
if [[ "$PHP_VER" == "7.2" ]]; then
|
||||
if [[ "$PHP_VER" == 7.2 ]]; then
|
||||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
elif [[ "$PHP_VER" == "7.3" ]]; then
|
||||
elif [[ "$PHP_VER" == 7.3 ]]; then
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp74
|
||||
elif [[ "$PHP_VER" == "7.4" ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp73
|
||||
elif [[ "$PHP_VER" == "7.0" ]]; then
|
||||
elif [[ "$PHP_VER" == 7.4 ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp74
|
||||
elif [[ "$PHP_VER" == 7.0 ]]; then
|
||||
# Install PHP 7.0 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp70
|
||||
fi
|
||||
else
|
||||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
# Install PHP 7.2 Dependencies by dangerous default - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
fi
|
||||
progress 4
|
||||
|
@ -800,6 +800,7 @@ x86_64-fedora-30
|
|||
x86_64-debian-stretch
|
||||
x86_64-debian-buster
|
||||
x86_64-ubuntu-bionic
|
||||
x86_64-ubuntu-focal
|
||||
x86_64-kali-2019.1
|
||||
x86_64-kali-2019.2
|
||||
x86_64-kali-2019.3
|
||||
|
@ -814,6 +815,7 @@ armv7l-debian-jessie
|
|||
armv7l-debian-stretch
|
||||
armv7l-debian-buster
|
||||
armv7l-ubuntu-bionic
|
||||
armv7l-ubuntu-focal
|
||||
"
|
||||
|
||||
# Check if we actually support this configuration
|
||||
|
@ -835,12 +837,18 @@ if [ "${FLAVOUR}" == "ubuntu" ]; then
|
|||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
fi
|
||||
if [ "${RELEASE}" == "20.04" ]; then
|
||||
echo "Install on Ubuntu 20.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
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"
|
||||
installSupported && exit || exit
|
||||
fi
|
||||
if [ "${RELEASE}" == "19.04" ]; then
|
||||
echo "Install on Ubuntu 19.04 under development."
|
||||
echo "Install on Ubuntu 19.04 partially supported bye."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
exit 1
|
||||
|
|
2
PyMISP
2
PyMISP
|
@ -1 +1 @@
|
|||
Subproject commit 0faa75824f4dbac2b14919bb17e9d0fef79026d7
|
||||
Subproject commit 5cc7a1ad57c2e5a90092644e61c3de1e3e449a36
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":4, "hotfix":124}
|
||||
{"major":2, "minor":4, "hotfix":125}
|
||||
|
|
|
@ -46,8 +46,8 @@ class AppController extends Controller
|
|||
|
||||
public $helpers = array('Utility', 'OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
|
||||
|
||||
private $__queryVersion = '104';
|
||||
public $pyMispVersion = '2.4.123';
|
||||
private $__queryVersion = '106';
|
||||
public $pyMispVersion = '2.4.125';
|
||||
public $phpmin = '7.2';
|
||||
public $phprec = '7.4';
|
||||
public $pythonmin = '3.6';
|
||||
|
@ -363,7 +363,11 @@ class AppController extends Controller
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->params['controller'] !== 'users' || !in_array($this->params['action'], array('login', 'register'))) {
|
||||
$pre_auth_actions = array('login', 'register');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$pre_auth_actions[] = 'email_otp';
|
||||
}
|
||||
if ($this->params['controller'] !== 'users' || !in_array($this->params['action'], $pre_auth_actions)) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Session->write('pre_login_requested_url', $this->here);
|
||||
}
|
||||
|
|
|
@ -151,6 +151,12 @@ class AttributesController extends AppController
|
|||
if (!isset($this->request->data['Attribute'])) {
|
||||
$this->request->data = array('Attribute' => $this->request->data);
|
||||
}
|
||||
if ($this->request->data['Attribute']['distribution'] == 4) {
|
||||
$sg = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1, $this->request->data['Attribute']['sharing_group_id']);
|
||||
if (empty($sg)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Sharing Group or not authorised.'));
|
||||
}
|
||||
}
|
||||
//
|
||||
// multiple attributes in batch import
|
||||
//
|
||||
|
@ -831,6 +837,12 @@ class AttributesController extends AppController
|
|||
if (!isset($this->request->data['Attribute'])) {
|
||||
$this->request->data = array('Attribute' => $this->request->data);
|
||||
}
|
||||
if ($this->request->data['Attribute']['distribution'] == 4) {
|
||||
$sg = $this->Attribute->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1, $this->request->data['Attribute']['sharing_group_id']);
|
||||
if (empty($sg)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Sharing Group or not authorised.'));
|
||||
}
|
||||
}
|
||||
$existingAttribute = $this->Attribute->findByUuid($this->Attribute->data['Attribute']['uuid']);
|
||||
// check if the attribute has a timestamp already set (from a previous instance that is trying to edit via synchronisation)
|
||||
// check which attribute is newer
|
||||
|
|
|
@ -285,21 +285,22 @@ class ACLComponent extends Component
|
|||
'view' => array('*')
|
||||
),
|
||||
'objects' => array(
|
||||
'add' => array('perm_add'),
|
||||
'addValueField' => array('perm_add'),
|
||||
'delete' => array('perm_add'),
|
||||
'edit' => array('perm_add'),
|
||||
'get_row' => array('perm_add'),
|
||||
'orphanedObjectDiagnostics' => array(),
|
||||
'editField' => array('perm_add'),
|
||||
'fetchEditForm' => array('perm_add'),
|
||||
'fetchViewValue' => array('*'),
|
||||
'quickAddAttributeForm' => array('perm_add'),
|
||||
'quickFetchTemplateWithValidObjectAttributes' => array('perm_add'),
|
||||
'proposeObjectsFromAttributes' => array('*'),
|
||||
'groupAttributesIntoObject' => array('perm_add'),
|
||||
'revise_object' => array('perm_add'),
|
||||
'view' => array('*'),
|
||||
'add' => array('perm_add'),
|
||||
'addValueField' => array('perm_add'),
|
||||
'delete' => array('perm_add'),
|
||||
'edit' => array('perm_add'),
|
||||
'get_row' => array('perm_add'),
|
||||
'orphanedObjectDiagnostics' => array(),
|
||||
'editField' => array('perm_add'),
|
||||
'fetchEditForm' => array('perm_add'),
|
||||
'fetchViewValue' => array('*'),
|
||||
'quickAddAttributeForm' => array('perm_add'),
|
||||
'quickFetchTemplateWithValidObjectAttributes' => array('perm_add'),
|
||||
'restSearch' => array('*'),
|
||||
'proposeObjectsFromAttributes' => array('*'),
|
||||
'groupAttributesIntoObject' => array('perm_add'),
|
||||
'revise_object' => array('perm_add'),
|
||||
'view' => array('*'),
|
||||
),
|
||||
'objectReferences' => array(
|
||||
'add' => array('perm_add'),
|
||||
|
@ -569,6 +570,7 @@ class ACLComponent extends Component
|
|||
'discardRegistrations' => array('perm_site_admin'),
|
||||
'downloadTerms' => array('*'),
|
||||
'edit' => array('*'),
|
||||
'email_otp' => array('*'),
|
||||
'searchGpgKey' => array('*'),
|
||||
'fetchGpgKey' => array('*'),
|
||||
'histogram' => array('*'),
|
||||
|
|
|
@ -72,7 +72,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'),
|
||||
'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'),
|
||||
'params' => array()
|
||||
)
|
||||
),
|
||||
|
@ -1080,7 +1080,7 @@ class RestResponseComponent extends Component
|
|||
'input' => 'radio',
|
||||
'type' => 'integer',
|
||||
'values' => array(1 => 'True', 0 => 'False' ),
|
||||
'help' => __('Will not return Attributes, shadow attribute and objects')
|
||||
'help' => __('Will only return the metadata of the given query scope, contained data is omitted.')
|
||||
),
|
||||
'minimal' => array(
|
||||
'input' => 'radio',
|
||||
|
@ -1456,13 +1456,13 @@ class RestResponseComponent extends Component
|
|||
'input' => 'select',
|
||||
'type' => 'integer',
|
||||
'operators' => ['equal', 'not_equal'],
|
||||
'values' => array( 1 => 'Hight', 2 => 'Medium', 3 => 'Low', 4 => 'Undefined')
|
||||
'values' => array( 1 => 'High', 2 => 'Medium', 3 => 'Low', 4 => 'Undefined')
|
||||
),
|
||||
'threatlevel' => array(
|
||||
'input' => 'select',
|
||||
'type' => 'integer',
|
||||
'operators' => ['equal', 'not_equal'],
|
||||
'values' => array( 1 => 'Hight', 2 => 'Medium', 3 => 'Low', 4 => 'Undefined')
|
||||
'values' => array( 1 => 'High', 2 => 'Medium', 3 => 'Low', 4 => 'Undefined')
|
||||
),
|
||||
'time' => array(
|
||||
'input' => 'text',
|
||||
|
|
|
@ -10,13 +10,14 @@ class RestSearchComponent extends Component
|
|||
'published', 'timestamp','enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
|
||||
'includeProposals', 'returnFormat', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'includeSightings', 'includeCorrelations', 'includeDecayScore',
|
||||
'decayingModel', 'excludeDecayed', 'modelOverrides', 'includeFullModel', 'score', 'attribute_timestamp', 'first_seen', 'last_seen'
|
||||
'decayingModel', 'excludeDecayed', 'modelOverrides', 'includeFullModel', 'score', 'attribute_timestamp', 'first_seen', 'last_seen',
|
||||
'threat_level_id'
|
||||
),
|
||||
'Event' => array(
|
||||
'returnFormat', 'value', 'type', 'category', 'org', 'tags', 'searchall', 'from', 'to', 'last', 'eventid', 'withAttachments',
|
||||
'metadata', 'uuid', 'publish_timestamp', 'timestamp', 'published', 'enforceWarninglist', 'sgReferenceOnly',
|
||||
'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits', 'attackGalaxy', 'to_ids', 'deleted',
|
||||
'excludeLocalTags', 'date', 'includeSightingdb', 'tag', 'object_relation'
|
||||
'excludeLocalTags', 'date', 'includeSightingdb', 'tag', 'object_relation', 'threat_level_id'
|
||||
),
|
||||
'Object' => array(
|
||||
'returnFormat', 'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
|
||||
|
|
|
@ -3835,7 +3835,7 @@ class EventsController extends AppController
|
|||
$this->Event->insertLock($this->Auth->user(), $id);
|
||||
$attributes = json_decode($this->request->data['Attribute']['JsonObject'], true);
|
||||
$default_comment = $this->request->data['Attribute']['default_comment'];
|
||||
$force = $this->request->data['Attribute']['force'];
|
||||
$force = $this->_isSiteAdmin() && $this->request->data['Attribute']['force'];
|
||||
$flashMessage = $this->Event->processFreeTextDataRouter($this->Auth->user(), $attributes, $id, $default_comment, $force);
|
||||
$this->Flash->info($flashMessage);
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'view', $id));
|
||||
|
@ -4530,16 +4530,20 @@ class EventsController extends AppController
|
|||
if (!in_array($type, $validTools)) {
|
||||
throw new MethodNotAllowedException('Invalid type.');
|
||||
}
|
||||
|
||||
App::uses('EventTimelineTool', 'Tools');
|
||||
$grapher = new EventTimelineTool();
|
||||
$data = $this->request->is('post') ? $this->request->data : array();
|
||||
$dataFiltering = array_key_exists('filtering', $data) ? $data['filtering'] : array();
|
||||
$scope = isset($data['scope']) ? $data['scope'] : 'seen';
|
||||
|
||||
$extended = isset($this->params['named']['extended']) ? 1 : 0;
|
||||
|
||||
$grapher->construct($this->Event, $this->Auth->user(), $dataFiltering, $extended);
|
||||
$json = $grapher->get_timeline($id);
|
||||
if ($scope == 'seen') {
|
||||
$json = $grapher->get_timeline($id);
|
||||
} elseif ($scope == 'sightings') {
|
||||
$json = $grapher->get_sighting_timeline($id);
|
||||
}
|
||||
|
||||
array_walk_recursive($json, function (&$item, $key) {
|
||||
if (!mb_detect_encoding($item, 'utf-8', true)) {
|
||||
|
|
|
@ -31,6 +31,9 @@ class UsersController extends AppController
|
|||
|
||||
// what pages are allowed for non-logged-in users
|
||||
$allowedActions = array('login', 'logout');
|
||||
if(!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$allowedActions[] = 'email_otp';
|
||||
}
|
||||
if (!empty(Configure::read('Security.allow_self_registration'))) {
|
||||
$allowedActions[] = 'register';
|
||||
}
|
||||
|
@ -1116,33 +1119,15 @@ class UsersController extends AppController
|
|||
$this->Auth->constructAuthenticate();
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post') && Configure::read('Security.email_otp_enabled')) {
|
||||
$user = $this->Auth->identify($this->request, $this->response);
|
||||
if ($user) {
|
||||
$this->Session->write('email_otp_user', $user);
|
||||
return $this->redirect('email_otp');
|
||||
}
|
||||
}
|
||||
if ($this->Auth->login()) {
|
||||
$this->User->extralog($this->Auth->user(), "login");
|
||||
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$user = $this->User->find('first', array(
|
||||
'conditions' => array(
|
||||
'User.id' => $this->Auth->user('id')
|
||||
),
|
||||
'recursive' => -1
|
||||
));
|
||||
$lastUserLogin = $user['User']['last_login'];
|
||||
unset($user['User']['password']);
|
||||
$user['User']['action'] = 'login';
|
||||
$user['User']['last_login'] = $this->Auth->user('current_login');
|
||||
$user['User']['current_login'] = time();
|
||||
$this->User->save($user['User'], true, array('id', 'last_login', 'current_login'));
|
||||
if (empty($this->Auth->authenticate['Form']['passwordHasher']) && !empty($passwordToSave)) {
|
||||
$this->User->saveField('password', $passwordToSave);
|
||||
}
|
||||
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
|
||||
if ($lastUserLogin) {
|
||||
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
|
||||
$this->Flash->info(sprintf('Welcome! Last login was on %s', $readableDatetime));
|
||||
}
|
||||
// no state changes are ever done via GET requests, so it is safe to return to the original page:
|
||||
$this->redirect($this->Auth->redirectUrl());
|
||||
// $this->redirect(array('controller' => 'events', 'action' => 'index'));
|
||||
$this->_postlogin();
|
||||
} else {
|
||||
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
|
||||
$dataSource = $dataSourceConfig['datasource'];
|
||||
|
@ -1224,6 +1209,35 @@ class UsersController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
private function _postlogin()
|
||||
{
|
||||
$this->User->extralog($this->Auth->user(), "login");
|
||||
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$user = $this->User->find('first', array(
|
||||
'conditions' => array(
|
||||
'User.id' => $this->Auth->user('id')
|
||||
),
|
||||
'recursive' => -1
|
||||
));
|
||||
$lastUserLogin = $user['User']['last_login'];
|
||||
unset($user['User']['password']);
|
||||
$user['User']['action'] = 'login';
|
||||
$user['User']['last_login'] = $this->Auth->user('current_login');
|
||||
$user['User']['current_login'] = time();
|
||||
$this->User->save($user['User'], true, array('id', 'last_login', 'current_login'));
|
||||
if (empty($this->Auth->authenticate['Form']['passwordHasher']) && !empty($passwordToSave)) {
|
||||
$this->User->saveField('password', $passwordToSave);
|
||||
}
|
||||
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
|
||||
if ($lastUserLogin) {
|
||||
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
|
||||
$this->Flash->info(__('Welcome! Last login was on %s', $readableDatetime));
|
||||
}
|
||||
// no state changes are ever done via GET requests, so it is safe to return to the original page:
|
||||
$this->redirect($this->Auth->redirectUrl());
|
||||
}
|
||||
|
||||
public function routeafterlogin()
|
||||
{
|
||||
// Events list
|
||||
|
@ -1656,6 +1670,90 @@ class UsersController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
public function email_otp()
|
||||
{
|
||||
$user = $this->Session->read('email_otp_user');
|
||||
if(empty($user)) {
|
||||
$this->redirect('login');
|
||||
}
|
||||
$redis = $this->User->setupRedis();
|
||||
$user_id = $user['id'];
|
||||
|
||||
if ($this->request->is('post') && isset($this->request->data['User']['otp'])) {
|
||||
$stored_otp = $redis->get('misp:otp:'.$user_id);
|
||||
if (!empty($stored_otp) && $this->request->data['User']['otp'] == $stored_otp) {
|
||||
// we invalidate the previously generated OTP
|
||||
$redis->delete('misp:otp:'.$user_id);
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
} else {
|
||||
$this->Flash->error(__("The OTP is incorrect or has expired"));
|
||||
}
|
||||
} else {
|
||||
// GET Request
|
||||
|
||||
// We check for exceptions
|
||||
$exception_list = Configure::read('Security.email_otp_exceptions');
|
||||
if (!empty($exception_list)) {
|
||||
$exceptions = explode(",", $exception_list);
|
||||
foreach ($exceptions as &$exception) {
|
||||
if ($user['email'] == trim($exception)) {
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadModel('Server');
|
||||
|
||||
// Generating the OTP
|
||||
$digits = !empty(Configure::read('Security.email_otp_length')) ? Configure::read('Security.email_otp_length') : $this->Server->serverSettings['Security']['email_otp_length']['value'];
|
||||
$otp = "";
|
||||
for ($i=0; $i<$digits; $i++) {
|
||||
$otp.= random_int(0,9);
|
||||
}
|
||||
// We use Redis to cache the OTP
|
||||
$redis->set('misp:otp:'.$user_id, $otp);
|
||||
$validity = !empty(Configure::read('Security.email_otp_validity')) ? Configure::read('Security.email_otp_validity') : $this->Server->serverSettings['Security']['email_otp_validity']['value'];
|
||||
$redis->expire('misp:otp:'.$user_id, (int) $validity * 60);
|
||||
|
||||
// Email construction
|
||||
$body = !empty(Configure::read('Security.email_otp_text')) ? 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 = str_replace('$validity', $validity, $body);
|
||||
$body = str_replace('$otp', $otp, $body);
|
||||
$body = str_replace('$ip', $this->__getClientIP(), $body);
|
||||
$body = str_replace('$username', $user['email'], $body);
|
||||
$result = $this->User->sendEmail(array('User' => $user), $body, false, "[MISP] Email OTP");
|
||||
|
||||
if ( $result ) {
|
||||
$this->Flash->success(__("An email containing a OTP has been sent."));
|
||||
} else {
|
||||
$this->Flash->error(__("The email couldn't be sent, please reach out to your administrator."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to determine the IP of a client (proxy aware)
|
||||
*/
|
||||
private function __getClientIP() {
|
||||
$x_forwarded = filter_input(INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_SANITIZE_STRING);
|
||||
$client_ip = filter_input(INPUT_SERVER, 'HTTP_CLIENT_IP', FILTER_SANITIZE_STRING);
|
||||
if (!empty($x_forwarded)) {
|
||||
$x_forwarded = explode(",", $x_forwarded);
|
||||
return $x_forwarded[0];
|
||||
} elseif(!empty($client_ip)){
|
||||
return $_client_ip;
|
||||
} else {
|
||||
return filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
// shows some statistics about the instance
|
||||
public function statistics($page = 'data')
|
||||
{
|
||||
|
@ -1742,6 +1840,7 @@ class UsersController extends AppController
|
|||
$stats['user_count_pgp'] = $this->User->find('count', array('recursive' => -1, 'conditions' => array('User.gpgkey !=' => '')));
|
||||
$stats['org_count'] = count($orgs);
|
||||
$stats['local_org_count'] = count($local_orgs);
|
||||
$stats['contributing_org_count'] = $this->User->Event->find('count', array('recursive' => -1, 'group' => array('Event.orgc_id')));
|
||||
$stats['average_user_per_org'] = round($stats['user_count'] / $stats['local_org_count'], 1);
|
||||
|
||||
$this->loadModel('Thread');
|
||||
|
@ -2275,7 +2374,7 @@ class UsersController extends AppController
|
|||
$requestObject['message'] = '';
|
||||
}
|
||||
if (empty($requestObject['email'])) {
|
||||
throw new InvalidArgumentException(__('We require at least the email field to be filled.'));
|
||||
throw new BadRequestException(__('We require at least the email field to be filled.'));
|
||||
}
|
||||
$this->loadModel('Inbox');
|
||||
$this->Inbox->create();
|
||||
|
@ -2374,8 +2473,13 @@ class UsersController extends AppController
|
|||
$id = $this->params['named']['id'];
|
||||
}
|
||||
$this->loadModel('Inbox');
|
||||
if (Validation::uuid($id)) {
|
||||
$id = $this->Toolbox->findIdByUuid($this->Inbox, $id);
|
||||
if (!is_array($id)) {
|
||||
$id = array($id);
|
||||
}
|
||||
foreach ($id as $k => $v) {
|
||||
if (Validation::uuid($v)) {
|
||||
$id[$k] = $this->Toolbox->findIdByUuid($this->Inbox, $v);
|
||||
}
|
||||
}
|
||||
$registrations = $this->Inbox->find('all', array(
|
||||
'recursive' => -1,
|
||||
|
@ -2400,7 +2504,7 @@ class UsersController extends AppController
|
|||
$this->Log->save(array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $id,
|
||||
'model_id' => 0,
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'discardRegistrations',
|
||||
'title' => $message,
|
||||
|
@ -2466,6 +2570,9 @@ class UsersController extends AppController
|
|||
'perm_admin' => $role['Role']['perm_admin']
|
||||
);
|
||||
}
|
||||
if (empty($this->request->data['User'])) {
|
||||
$this->request->data = array('User' => $this->request->data);
|
||||
}
|
||||
if (!empty($default_role)) {
|
||||
$this->request->data['User']['role_id'] = $default_role['Role']['id'];
|
||||
}
|
||||
|
@ -2492,11 +2599,11 @@ class UsersController extends AppController
|
|||
if (!empty($default_role)) {
|
||||
$this->request->data['User']['role_id'] = $default_role['Role']['id'];
|
||||
} else {
|
||||
throw new InvalidArgumentException(__('Role ID not provided and no default role exist on the instance'));
|
||||
throw new BadRequestException(__('Role ID not provided and no default role exist on the instance'));
|
||||
}
|
||||
}
|
||||
if (!isset($this->request->data['User']['org_id'])) {
|
||||
throw new InvalidArgumentException(__('No organisation selected. Supply an Organisation ID'));
|
||||
throw new BadRequestException(__('No organisation selected. Supply an Organisation ID'));
|
||||
} else {
|
||||
if (Validation::uuid($this->request->data['User']['org_id'])) {
|
||||
$id = $this->Toolbox->findIdByUuid($this->User->Organisation, $this->request->data['User']['org_id']);
|
||||
|
|
|
@ -12,7 +12,7 @@ class JsonExport
|
|||
} else if($options['scope'] === 'Event') {
|
||||
return $this->__eventHandler($data, $options);
|
||||
} else if($options['scope'] === 'Object') {
|
||||
return $this->__eventHandler($data, $options);
|
||||
return $this->__objectHandler($data, $options);
|
||||
} else if($options['scope'] === 'Sighting') {
|
||||
return $this->__sightingsHandler($data, $options);
|
||||
}
|
||||
|
|
|
@ -135,4 +135,145 @@
|
|||
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extrapolation strategy:
|
||||
* - If only positive sightings: Will be from first to last sighting
|
||||
* - If both positive and false positive: False positive get priority. It will be marked as false positive until next positive sighting
|
||||
*/
|
||||
public function get_sighting_timeline($id)
|
||||
{
|
||||
$event = $this->__eventModel->fetchEvent($this->__user, array(
|
||||
'eventid' => $id,
|
||||
'flatten' => 1,
|
||||
'includeTagRelations' => 1,
|
||||
'extended' => $this->__extended_view
|
||||
));
|
||||
$this->__json['items'] = array();
|
||||
|
||||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
} else {
|
||||
$event = $event[0];
|
||||
}
|
||||
|
||||
$lookupAttribute = array();
|
||||
foreach ($event['Attribute'] as $k => $attribute) {
|
||||
$lookupAttribute[$attribute['id']] = &$event['Attribute'][$k];
|
||||
}
|
||||
|
||||
// regroup sightings per attribute
|
||||
$regroupedSightings = array();
|
||||
foreach ($event['Sighting'] as $k => $sighting) {
|
||||
$event['Sighting'][$k]['date_sighting'] *= 1000; // adapt to use micro
|
||||
$regroupedSightings[$sighting['attribute_id']][] = &$event['Sighting'][$k];
|
||||
}
|
||||
// make sure sightings are ordered
|
||||
uksort($regroupedSightings, function ($a, $b) {
|
||||
return $a['date_sighting'] > $b['date_sighting'];
|
||||
});
|
||||
// generate extrapolation
|
||||
$now = time()*1000;
|
||||
foreach ($regroupedSightings as $attributeId => $sightings) {
|
||||
$i = 0;
|
||||
while ($i < count($sightings)) {
|
||||
$sighting = $sightings[$i];
|
||||
$attribute = $lookupAttribute[$attributeId];
|
||||
$fpSightingIndex = $this->getNextFalsePositiveSightingIndex($sightings, $i+1);
|
||||
if ($fpSightingIndex === false) { // No next FP, extrapolate to now
|
||||
$this->__json['items'][] = array(
|
||||
'attribute_id' => $attributeId,
|
||||
'id' => sprintf('%s-%s', $attributeId, $sighting['id']),
|
||||
'uuid' => $sighting['uuid'],
|
||||
'content' => $attribute['value'],
|
||||
'event_id' => $attribute['event_id'],
|
||||
'group' => 'sighting_positive',
|
||||
'timestamp' => $attribute['timestamp'],
|
||||
'first_seen' => $sighting['date_sighting'],
|
||||
'last_seen' => $now,
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
// set up until last positive
|
||||
$pSightingIndex = $fpSightingIndex - 1;
|
||||
$halfTime = 0;
|
||||
if ($pSightingIndex == $i) {
|
||||
// we have only one positive sighting, thus the UP time should be take from a pooling frequence
|
||||
// for now, consider it UP only for half the time until the next FP
|
||||
$halfTime = ($sightings[$i+1]['date_sighting'] - $sighting['date_sighting'])/2;
|
||||
}
|
||||
$pSighting = $sightings[$pSightingIndex];
|
||||
$this->__json['items'][] = array(
|
||||
'attribute_id' => $attributeId,
|
||||
'id' => sprintf('%s-%s', $attributeId, $sighting['id']),
|
||||
'uuid' => $sighting['uuid'],
|
||||
'content' => $attribute['value'],
|
||||
'event_id' => $attribute['event_id'],
|
||||
'group' => 'sighting_positive',
|
||||
'timestamp' => $attribute['timestamp'],
|
||||
'first_seen' => $sighting['date_sighting'],
|
||||
'last_seen' => $pSighting['date_sighting'] + $halfTime,
|
||||
);
|
||||
// No next FP, extrapolate to now
|
||||
$fpSighting = $sightings[$fpSightingIndex];
|
||||
$secondNextPSightingIndex = $this->getNextPositiveSightingIndex($sightings, $fpSightingIndex+1);
|
||||
if ($secondNextPSightingIndex === false) { // No next P, extrapolate to now
|
||||
$this->__json['items'][] = array(
|
||||
'attribute_id' => $attributeId,
|
||||
'id' => sprintf('%s-%s', $attributeId, $sighting['id']),
|
||||
'uuid' => $sighting['uuid'],
|
||||
'content' => $attribute['value'],
|
||||
'event_id' => $attribute['event_id'],
|
||||
'group' => 'sighting_negative',
|
||||
'timestamp' => $attribute['timestamp'],
|
||||
'first_seen' => $pSighting['date_sighting'] - $halfTime,
|
||||
'last_seen' => $now,
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
if ($halfTime > 0) { // We need to fake a previous P
|
||||
$pSightingIndex = $pSightingIndex+1;
|
||||
$pSighting = $sightings[$pSightingIndex];
|
||||
}
|
||||
// set down until next postive
|
||||
$secondNextPSighting = $sightings[$secondNextPSightingIndex];
|
||||
$this->__json['items'][] = array(
|
||||
'attribute_id' => $attributeId,
|
||||
'id' => sprintf('%s-%s', $attributeId, $sighting['id']),
|
||||
'uuid' => $pSighting['uuid'],
|
||||
'content' => $attribute['value'],
|
||||
'event_id' => $attribute['event_id'],
|
||||
'group' => 'sighting_negative',
|
||||
'timestamp' => $attribute['timestamp'],
|
||||
'first_seen' => $pSighting['date_sighting'] - $halfTime,
|
||||
'last_seen' => $secondNextPSighting['date_sighting'],
|
||||
);
|
||||
$i = $secondNextPSightingIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->__json;
|
||||
}
|
||||
|
||||
private function getNextFalsePositiveSightingIndex($sightings, $startIndex)
|
||||
{
|
||||
for ($i=$startIndex; $i < count($sightings) ; $i++) {
|
||||
$sighting = $sightings[$i];
|
||||
if ($sighting['type'] == 1) { // is false positive
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private function getNextPositiveSightingIndex($sightings, $startIndex)
|
||||
{
|
||||
for ($i=$startIndex; $i < count($sightings) ; $i++) {
|
||||
$sighting = $sightings[$i];
|
||||
if ($sighting['type'] == 0) { // is false positive
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,10 +45,12 @@ class JSONConverterTool
|
|||
unset($event['Event']['SharingGroup']);
|
||||
}
|
||||
if ($object == 'Galaxy') {
|
||||
foreach ($event['Event']['Galaxy'] as $k => $galaxy) {
|
||||
foreach ($galaxy['GalaxyCluster'] as $k2 => $cluster) {
|
||||
if (empty($cluster['meta'])) {
|
||||
$event['Event']['Galaxy'][$k]['GalaxyCluster'][$k2]['meta'] = new stdclass();
|
||||
if (!empty($event['Event']['Galaxy'])) {
|
||||
foreach ($event['Event']['Galaxy'] as $k => $galaxy) {
|
||||
foreach ($galaxy['GalaxyCluster'] as $k2 => $cluster) {
|
||||
if (empty($cluster['meta'])) {
|
||||
$event['Event']['Galaxy'][$k]['GalaxyCluster'][$k2]['meta'] = new stdclass();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -78,7 +78,7 @@ class AppModel extends Model
|
|||
33 => false, 34 => false, 35 => false, 36 => false, 37 => false, 38 => false,
|
||||
39 => false, 40 => false, 41 => false, 42 => false, 43 => false, 44 => false,
|
||||
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
|
||||
51 => false
|
||||
51 => false, 52 => false, 53 => false
|
||||
);
|
||||
|
||||
public $advanced_updates_description = array(
|
||||
|
@ -1268,7 +1268,7 @@ class AppModel extends Model
|
|||
case 39:
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS user_settings (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`setting` varchar(255) COLLATE utf8_bin NOT NULL,
|
||||
`value` text,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`timestamp` int(11) NOT NULL,
|
||||
|
@ -1377,6 +1377,18 @@ class AppModel extends Model
|
|||
$sqlArray[] = "ALTER TABLE `feeds` ADD `orgc_id` int(11) NOT NULL DEFAULT 0";
|
||||
$this->__addIndex('feeds', 'orgc_id');
|
||||
break;
|
||||
case 52:
|
||||
if (!empty($this->query("SHOW COLUMNS FROM `admin_settings` LIKE 'key';"))) {
|
||||
$sqlArray[] = "ALTER TABLE admin_settings CHANGE `key` `setting` varchar(255) COLLATE utf8_bin NOT NULL;";
|
||||
$this->__addIndex('admin_settings', 'setting');
|
||||
}
|
||||
break;
|
||||
case 53:
|
||||
if (!empty($this->query("SHOW COLUMNS FROM `user_settings` LIKE 'key';"))) {
|
||||
$sqlArray[] = "ALTER TABLE user_settings CHANGE `key` `setting` varchar(255) COLLATE utf8_bin NOT NULL;";
|
||||
$this->__addIndex('user_settings', 'setting');
|
||||
}
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
@ -2852,4 +2864,21 @@ class AppModel extends Model
|
|||
|
||||
return $this->log($message, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random file name in tmp dir.
|
||||
* @return string
|
||||
*/
|
||||
protected function tempFileName()
|
||||
{
|
||||
return $this->tempDir() . DS . $this->generateRandomFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function tempDir()
|
||||
{
|
||||
return Configure::read('MISP.tmpdir') ?: sys_get_temp_dir();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -699,7 +699,7 @@ class Attribute extends AppModel
|
|||
* Only recorrelate if:
|
||||
* - We are dealing with a new attribute OR
|
||||
* - The existing attribute's previous state is known AND
|
||||
* value, type or disable correlation have changed
|
||||
* value, type, disable correlation or distribution have changed
|
||||
* This will avoid recorrelations when it's not really needed, such as adding a tag
|
||||
*/
|
||||
if (!$created) {
|
||||
|
@ -707,7 +707,9 @@ class Attribute extends AppModel
|
|||
empty($this->old) ||
|
||||
$this->data['Attribute']['value'] != $this->old['Attribute']['value'] ||
|
||||
$this->data['Attribute']['disable_correlation'] != $this->old['Attribute']['disable_correlation'] ||
|
||||
$this->data['Attribute']['type'] != $this->old['Attribute']['type']
|
||||
$this->data['Attribute']['type'] != $this->old['Attribute']['type'] ||
|
||||
$this->data['Attribute']['distribution'] != $this->old['Attribute']['distribution'] ||
|
||||
$this->data['Attribute']['sharing_group_id'] != $this->old['Attribute']['sharing_group_id']
|
||||
) {
|
||||
$this->__beforeSaveCorrelation($this->data['Attribute']);
|
||||
$this->__afterSaveCorrelation($this->data['Attribute'], false, $passedEvent);
|
||||
|
@ -4414,7 +4416,8 @@ class Attribute extends AppModel
|
|||
'event_timestamp' => array('function' => 'set_filter_timestamp', 'pop' => true),
|
||||
'publish_timestamp' => array('function' => 'set_filter_timestamp'),
|
||||
'org' => array('function' => 'set_filter_org'),
|
||||
'published' => array('function' => 'set_filter_published')
|
||||
'published' => array('function' => 'set_filter_published'),
|
||||
'threat_level_id' => array('function' => 'set_filter_threat_level_id')
|
||||
),
|
||||
'Object' => array(
|
||||
'object_name' => array('function' => 'set_filter_object_name'),
|
||||
|
|
|
@ -641,6 +641,12 @@ class Event extends AppModel
|
|||
if (isset($this->data['Event']['info'])) {
|
||||
$this->Correlation->updateAll(array('Correlation.info' => $db->value($this->data['Event']['info'])), array('Correlation.event_id' => intval($this->data['Event']['id'])));
|
||||
}
|
||||
if (isset($this->data['Event']['distribution'])) {
|
||||
$this->Correlation->updateAll(array('Correlation.distribution' => $db->value($this->data['Event']['distribution'])), array('Correlation.event_id' => intval($this->data['Event']['id'])));
|
||||
}
|
||||
if (isset($this->data['Event']['sharing_group_id'])) {
|
||||
$this->Correlation->updateAll(array('Correlation.sharing_group_id' => $db->value($this->data['Event']['sharing_group_id'])), array('Correlation.event_id' => intval($this->data['Event']['id'])));
|
||||
}
|
||||
}
|
||||
if (empty($this->data['Event']['unpublishAction']) && empty($this->data['Event']['skip_zmq']) && Configure::read('Plugin.ZeroMQ_enable') && Configure::read('Plugin.ZeroMQ_event_notifications_enable')) {
|
||||
$pubSubTool = $this->getPubSubTool();
|
||||
|
@ -1652,7 +1658,8 @@ class Event extends AppModel
|
|||
'publish_timestamp' => array('function' => 'set_filter_timestamp', 'pop' => true),
|
||||
'org' => array('function' => 'set_filter_org', 'pop' => true),
|
||||
'uuid' => array('function' => 'set_filter_uuid', 'pop' => true),
|
||||
'published' => array('function' => 'set_filter_published', 'pop' => true)
|
||||
'published' => array('function' => 'set_filter_published', 'pop' => true),
|
||||
'threat_level_id' => array('function' => 'set_filter_threat_level_id', 'pop' => true)
|
||||
),
|
||||
'Object' => array(
|
||||
'object_name' => array('function' => 'set_filter_object_name'),
|
||||
|
@ -2150,6 +2157,22 @@ class Event extends AppModel
|
|||
'Object' => array('name', 'meta-category')
|
||||
);
|
||||
foreach ($results as $eventKey => &$event) {
|
||||
if ($event['Event']['distribution'] == 4 && !in_array($event['Event']['sharing_group_id'], $sgids)) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$this->Log->save(array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'Event',
|
||||
'model_id' => $event['Event']['id'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'fetchEvent',
|
||||
'user_id' => $user['id'],
|
||||
'title' => 'User was able to fetch the event but not the sharing_group it belongs to',
|
||||
'change' => ''
|
||||
));
|
||||
unset($results[$eventKey]); // Current user cannot access sharing_group associated to this event
|
||||
continue;
|
||||
}
|
||||
$this->__attachReferences($user, $event, $sgids, $fields);
|
||||
$event = $this->Orgc->attachOrgsToEvent($event, $fieldsOrg);
|
||||
if (!$options['sgReferenceOnly'] && $event['Event']['sharing_group_id']) {
|
||||
|
@ -2448,7 +2471,11 @@ class Event extends AppModel
|
|||
}
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v['distribution'] == 4) {
|
||||
$data[$k]['SharingGroup'] = $sharingGroupData[$v['sharing_group_id']]['SharingGroup'];
|
||||
if (isset($sharingGroupData[$v['sharing_group_id']])) {
|
||||
$data[$k]['SharingGroup'] = $sharingGroupData[$v['sharing_group_id']]['SharingGroup'];
|
||||
} else {
|
||||
unset($data[$k]); // current user could not fetch the sharing_group
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
@ -2689,6 +2716,14 @@ class Event extends AppModel
|
|||
return $conditions;
|
||||
}
|
||||
|
||||
public function set_filter_threat_level_id(&$params, $conditions, $options)
|
||||
{
|
||||
if (isset($params['threat_level_id'])) {
|
||||
$conditions['AND']['Event.threat_level_id'] = $params['threat_level_id'];
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
public function set_filter_tags(&$params, $conditions, $options)
|
||||
{
|
||||
if (!empty($params['tags'])) {
|
||||
|
@ -3237,10 +3272,10 @@ class Event extends AppModel
|
|||
return array($bodyevent, $body);
|
||||
}
|
||||
|
||||
private function __captureSGForElement($element, $user)
|
||||
private function __captureSGForElement($element, $user, $syncLocal=false)
|
||||
{
|
||||
if (isset($element['SharingGroup'])) {
|
||||
$sg = $this->SharingGroup->captureSG($element['SharingGroup'], $user);
|
||||
$sg = $this->SharingGroup->captureSG($element['SharingGroup'], $user, $syncLocal);
|
||||
unset($element['SharingGroup']);
|
||||
} elseif (isset($element['sharing_group_id'])) {
|
||||
$sg = $this->SharingGroup->checkIfAuthorised($user, $element['sharing_group_id']) ? $element['sharing_group_id'] : false;
|
||||
|
@ -3257,17 +3292,17 @@ class Event extends AppModel
|
|||
|
||||
// When we receive an event via REST, we might end up with organisations, sharing groups, tags that we do not know
|
||||
// or which we need to update. All of that is controlled in this method.
|
||||
private function __captureObjects($data, $user)
|
||||
private function __captureObjects($data, $user, $syncLocal=false)
|
||||
{
|
||||
// First we need to check whether the event or any attributes are tied to a sharing group and whether the user is even allowed to create the sharing group / is part of it
|
||||
if (isset($data['Event']['distribution']) && $data['Event']['distribution'] == 4) {
|
||||
$data['Event'] = $this->__captureSGForElement($data['Event'], $user);
|
||||
$data['Event'] = $this->__captureSGForElement($data['Event'], $user, $syncLocal);
|
||||
}
|
||||
if (!empty($data['Event']['Attribute'])) {
|
||||
foreach ($data['Event']['Attribute'] as $k => $a) {
|
||||
unset($data['Event']['Attribute']['id']);
|
||||
if (isset($a['distribution']) && $a['distribution'] == 4) {
|
||||
$data['Event']['Attribute'][$k] = $this->__captureSGForElement($a, $user);
|
||||
$data['Event']['Attribute'][$k] = $this->__captureSGForElement($a, $user, $syncLocal);
|
||||
if ($data['Event']['Attribute'][$k] === false) {
|
||||
unset($data['Event']['Attribute']);
|
||||
}
|
||||
|
@ -3277,7 +3312,7 @@ class Event extends AppModel
|
|||
if (!empty($data['Event']['Object'])) {
|
||||
foreach ($data['Event']['Object'] as $k => $o) {
|
||||
if (isset($o['distribution']) && $o['distribution'] == 4) {
|
||||
$data['Event']['Object'][$k] = $this->__captureSGForElement($o, $user);
|
||||
$data['Event']['Object'][$k] = $this->__captureSGForElement($o, $user, $syncLocal);
|
||||
if ($data['Event']['Object'][$k] === false) {
|
||||
unset($data['Event']['Object'][$k]);
|
||||
continue;
|
||||
|
@ -3285,7 +3320,7 @@ class Event extends AppModel
|
|||
}
|
||||
foreach ($o['Attribute'] as $k2 => $a) {
|
||||
if (isset($a['distribution']) && $a['distribution'] == 4) {
|
||||
$data['Event']['Object'][$k]['Attribute'][$k2] = $this->__captureSGForElement($a, $user);
|
||||
$data['Event']['Object'][$k]['Attribute'][$k2] = $this->__captureSGForElement($a, $user, $syncLocal);
|
||||
if ($data['Event']['Object'][$k]['Attribute'][$k2] === false) {
|
||||
unset($data['Event']['Object'][$k]['Attribute'][$k2]);
|
||||
}
|
||||
|
@ -3453,6 +3488,24 @@ class Event extends AppModel
|
|||
return 'blocked';
|
||||
}
|
||||
}
|
||||
if ($passAlong) {
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$server = $this->Server->find('first', array(
|
||||
'conditions' => array(
|
||||
'Server.id' => $passAlong
|
||||
),
|
||||
'recursive' => -1,
|
||||
'fields' => array(
|
||||
'Server.name',
|
||||
'Server.id',
|
||||
'Server.unpublish_event',
|
||||
'Server.publish_without_email',
|
||||
'Server.internal'
|
||||
)
|
||||
));
|
||||
} else {
|
||||
$server['Server']['internal'] = false;
|
||||
}
|
||||
if ($fromXml) {
|
||||
// Workaround for different structure in XML/array than what CakePHP expects
|
||||
$data = $this->cleanupEventArrayFromXML($data);
|
||||
|
@ -3479,7 +3532,7 @@ class Event extends AppModel
|
|||
return $existingEvent['Event']['id'];
|
||||
} else {
|
||||
if ($fromXml) {
|
||||
$data = $this->__captureObjects($data, $user);
|
||||
$data = $this->__captureObjects($data, $user, $server['Server']['internal']);
|
||||
}
|
||||
if ($data === false) {
|
||||
$failedCapture = true;
|
||||
|
@ -3487,7 +3540,7 @@ class Event extends AppModel
|
|||
}
|
||||
} else {
|
||||
if ($fromXml) {
|
||||
$data = $this->__captureObjects($data, $user);
|
||||
$data = $this->__captureObjects($data, $user, $server['Server']['internal']);
|
||||
}
|
||||
if ($data === false) {
|
||||
$failedCapture = true;
|
||||
|
@ -3548,19 +3601,6 @@ class Event extends AppModel
|
|||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($saveResult) {
|
||||
if ($passAlong) {
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$server = $this->Server->find('first', array(
|
||||
'conditions' => array(
|
||||
'Server.id' => $passAlong
|
||||
),
|
||||
'recursive' => -1,
|
||||
'fields' => array(
|
||||
'Server.name',
|
||||
'Server.id',
|
||||
'Server.unpublish_event',
|
||||
'Server.publish_without_email'
|
||||
)
|
||||
));
|
||||
if ($server['Server']['publish_without_email'] == 0) {
|
||||
$st = "enabled";
|
||||
} else {
|
||||
|
@ -3703,6 +3743,23 @@ class Event extends AppModel
|
|||
} else {
|
||||
$existingEvent = $this->findById($id);
|
||||
}
|
||||
if ($passAlong) {
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$server = $this->Server->find('first', array(
|
||||
'conditions' => array(
|
||||
'Server.id' => $passAlong
|
||||
),
|
||||
'recursive' => -1,
|
||||
'fields' => array(
|
||||
'Server.name',
|
||||
'Server.id',
|
||||
'Server.unpublish_event',
|
||||
'Server.publish_without_email'
|
||||
)
|
||||
));
|
||||
} else {
|
||||
$server['Server']['internal'] = false;
|
||||
}
|
||||
// If the event exists...
|
||||
$dateObj = new DateTime();
|
||||
$date = $dateObj->getTimestamp();
|
||||
|
@ -3725,7 +3782,7 @@ class Event extends AppModel
|
|||
return(array('error' => 'Event could not be saved: Invalid sharing group or you don\'t have access to that sharing group.'));
|
||||
}
|
||||
} else {
|
||||
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user);
|
||||
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user, $server['Server']['internal']);
|
||||
unset($data['Event']['SharingGroup']);
|
||||
if ($data['Event']['sharing_group_id'] === false) {
|
||||
return (array('error' => 'Event could not be saved: User not authorised to create the associated sharing group.'));
|
||||
|
@ -3846,19 +3903,6 @@ class Event extends AppModel
|
|||
if ((!empty($data['Event']['published']) && 1 == $data['Event']['published'])) {
|
||||
// The edited event is from a remote server ?
|
||||
if ($passAlong) {
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$server = $this->Server->find('first', array(
|
||||
'conditions' => array(
|
||||
'Server.id' => $passAlong
|
||||
),
|
||||
'recursive' => -1,
|
||||
'fields' => array(
|
||||
'Server.name',
|
||||
'Server.id',
|
||||
'Server.unpublish_event',
|
||||
'Server.publish_without_email'
|
||||
)
|
||||
));
|
||||
if ($server['Server']['publish_without_email'] == 0) {
|
||||
$st = "enabled";
|
||||
} else {
|
||||
|
|
|
@ -247,10 +247,7 @@ class Feed extends AppModel
|
|||
$data = $this->feedGetUri($feed, $feedUrl, $HttpSocket, true);
|
||||
|
||||
if (!$isLocal) {
|
||||
$redis = $this->setupRedis();
|
||||
if ($redis === false) {
|
||||
throw new Exception('Could not reach Redis.');
|
||||
}
|
||||
$redis = $this->setupRedisWithException();
|
||||
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
|
||||
file_put_contents($feedCache, $data);
|
||||
}
|
||||
|
@ -502,12 +499,17 @@ class Feed extends AppModel
|
|||
|
||||
$result = array(
|
||||
'header' => array(
|
||||
'Accept' => array('application/json', 'text/plain'),
|
||||
'Content-Type' => 'application/json',
|
||||
'MISP-version' => $version,
|
||||
'MISP-uuid' => Configure::read('MISP.uuid')
|
||||
'Accept' => array('application/json', 'text/plain'),
|
||||
'MISP-version' => $version,
|
||||
'MISP-uuid' => Configure::read('MISP.uuid'),
|
||||
)
|
||||
);
|
||||
|
||||
// Enable gzipped responses if PHP has 'gzdecode' method
|
||||
if (function_exists('gzdecode')) {
|
||||
$result['header']['Accept-Encoding'] = 'gzip';
|
||||
}
|
||||
|
||||
if ($commit) {
|
||||
$result['header']['commit'] = $commit;
|
||||
}
|
||||
|
@ -1590,24 +1592,51 @@ class Feed extends AppModel
|
|||
if ($data === false) {
|
||||
throw new Exception("Could not read local file '$uri'.");
|
||||
}
|
||||
return $data;
|
||||
} else {
|
||||
throw new Exception("Local file '$uri' doesn't exists.");
|
||||
}
|
||||
}
|
||||
|
||||
$request = $this->__createFeedRequest($feed['Feed']['headers']);
|
||||
|
||||
if ($followRedirect) {
|
||||
$response = $this->getFollowRedirect($HttpSocket, $uri, $request);
|
||||
} else {
|
||||
$request = $this->__createFeedRequest($feed['Feed']['headers']);
|
||||
$response = $HttpSocket->get($uri, array(), $request);
|
||||
}
|
||||
|
||||
if ($followRedirect) {
|
||||
$response = $this->getFollowRedirect($HttpSocket, $uri, $request);
|
||||
} else {
|
||||
$response = $HttpSocket->get($uri, array(), $request);
|
||||
}
|
||||
if ($response === false) {
|
||||
throw new Exception("Could not reach '$uri'.");
|
||||
} else if ($response->code != 200) { // intentionally !=
|
||||
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
|
||||
}
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Could not reach '$uri'.");
|
||||
} else if ($response->code != 200) { // intentionally !=
|
||||
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
|
||||
$data = $response->body;
|
||||
|
||||
$contentEncoding = $response->getHeader('Content-Encoding');
|
||||
if ($contentEncoding === 'gzip') {
|
||||
$data = gzdecode($data);
|
||||
if ($data === false) {
|
||||
throw new Exception("Fetching the '$uri' failed, response should be gzip encoded, but gzip decoding failed.");
|
||||
}
|
||||
} else if ($contentEncoding) {
|
||||
throw new Exception("Fetching the '$uri' failed, because remote server returns unsupported content encoding '$contentEncoding'");
|
||||
}
|
||||
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
if ($contentType === 'application/zip') {
|
||||
$zipFile = new File($this->tempFileName());
|
||||
$zipFile->write($data);
|
||||
$zipFile->close();
|
||||
|
||||
try {
|
||||
$data = $this->unzipFirstFile($zipFile);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Fetching the '$uri' failed: {$e->getMessage()}");
|
||||
} finally {
|
||||
$zipFile->delete();
|
||||
}
|
||||
$data = $response->body;
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@ -1720,4 +1749,47 @@ class Feed extends AppModel
|
|||
$this->save($feed);
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File $zipFile
|
||||
* @return string Uncompressed data
|
||||
* @throws Exception
|
||||
*/
|
||||
private function unzipFirstFile(File $zipFile)
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
throw new Exception("ZIP archive decompressing is not supported.");
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$result = $zip->open($zipFile->pwd());
|
||||
if ($result !== true) {
|
||||
throw new Exception("Remote server returns ZIP file, that cannot be open (error $result)");
|
||||
}
|
||||
|
||||
if ($zip->numFiles !== 1) {
|
||||
throw new Exception("Remote server returns ZIP file, that contains multiple files.");
|
||||
}
|
||||
|
||||
$filename = $zip->getNameIndex(0);
|
||||
if ($filename === false) {
|
||||
throw new Exception("Remote server returns ZIP file, but there is a problem with reading filename.");
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
$destinationFile = $this->tempFileName();
|
||||
$result = copy("zip://{$zipFile->pwd()}#$filename", $destinationFile);
|
||||
if ($result === false) {
|
||||
throw new Exception("Remote server returns ZIP file, that contains '$filename' file, that cannot be extracted.");
|
||||
}
|
||||
|
||||
$unzipped = new File($destinationFile);
|
||||
$data = $unzipped->read();
|
||||
if ($data === false) {
|
||||
throw new Exception("Couldn't read extracted file content.");
|
||||
}
|
||||
$unzipped->delete();
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -506,7 +506,10 @@ class MispObject extends AppModel
|
|||
)
|
||||
),
|
||||
);
|
||||
if (empty($options['includeAllTags'])) {
|
||||
if (!empty($options['metadata'])) {
|
||||
unset($params['contain']['Attribute']);
|
||||
}
|
||||
if (empty($options['metadata']) && empty($options['includeAllTags'])) {
|
||||
$params['contain']['Attribute']['AttributeTag']['Tag']['conditions']['exportable'] = 1;
|
||||
}
|
||||
if (isset($options['contain'])) {
|
||||
|
@ -514,7 +517,12 @@ class MispObject extends AppModel
|
|||
} else {
|
||||
$option['contain']['Event']['fields'] = array('id', 'info', 'org_id', 'orgc_id');
|
||||
}
|
||||
if (Configure::read('MISP.proposals_block_attributes') && isset($options['conditions']['AND']['Attribute.to_ids']) && $options['conditions']['AND']['Attribute.to_ids'] == 1) {
|
||||
if (
|
||||
empty($options['metadata']) &&
|
||||
Configure::read('MISP.proposals_block_attributes') &&
|
||||
isset($options['conditions']['AND']['Attribute.to_ids']) &&
|
||||
$options['conditions']['AND']['Attribute.to_ids'] == 1
|
||||
) {
|
||||
$this->Attribute->bindModel(array('hasMany' => array('ShadowAttribute' => array('foreignKey' => 'old_id'))));
|
||||
$proposalRestriction = array(
|
||||
'ShadowAttribute' => array(
|
||||
|
@ -544,7 +552,7 @@ class MispObject extends AppModel
|
|||
if (!isset($options['enforceWarninglist'])) {
|
||||
$options['enforceWarninglist'] = false;
|
||||
}
|
||||
if (!$user['Role']['perm_sync'] || !isset($options['deleted']) || !$options['deleted']) {
|
||||
if (empty($options['metadata']) && (!$user['Role']['perm_sync'] || !isset($options['deleted']) || !$options['deleted'])) {
|
||||
$params['contain']['Attribute']['conditions']['AND']['Attribute.deleted'] = 0;
|
||||
}
|
||||
if (isset($options['group'])) {
|
||||
|
@ -566,23 +574,25 @@ class MispObject extends AppModel
|
|||
}
|
||||
$results = array_values($results);
|
||||
$proposals_block_attributes = Configure::read('MISP.proposals_block_attributes');
|
||||
foreach ($results as $key => $objects) {
|
||||
foreach ($objects as $key2 => $attribute) {
|
||||
if ($options['enforceWarninglist'] && !$this->Warninglist->filterWarninglistAttributes($warninglists, $attribute['Attribute'], $this->Warninglist)) {
|
||||
unset($results[$key][$key2]);
|
||||
continue;
|
||||
}
|
||||
if ($proposals_block_attributes) {
|
||||
if (!empty($attribute['ShadowAttribute'])) {
|
||||
if (empty($options['metadata'])) {
|
||||
foreach ($results as $key => $objects) {
|
||||
foreach ($objects as $key2 => $attribute) {
|
||||
if ($options['enforceWarninglist'] && !$this->Warninglist->filterWarninglistAttributes($warninglists, $attribute['Attribute'], $this->Warninglist)) {
|
||||
unset($results[$key][$key2]);
|
||||
} else {
|
||||
unset($results[$key][$key2]['ShadowAttribute']);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($options['withAttachments']) {
|
||||
if ($this->typeIsAttachment($attribute['Attribute']['type'])) {
|
||||
$encodedFile = $this->base64EncodeAttachment($attribute['Attribute']);
|
||||
$results[$key][$key2]['Attribute']['data'] = $encodedFile;
|
||||
if ($proposals_block_attributes) {
|
||||
if (!empty($attribute['ShadowAttribute'])) {
|
||||
unset($results[$key][$key2]);
|
||||
} else {
|
||||
unset($results[$key][$key2]['ShadowAttribute']);
|
||||
}
|
||||
}
|
||||
if ($options['withAttachments']) {
|
||||
if ($this->typeIsAttachment($attribute['Attribute']['type'])) {
|
||||
$encodedFile = $this->base64EncodeAttachment($attribute['Attribute']);
|
||||
$results[$key][$key2]['Attribute']['data'] = $encodedFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1344,7 +1354,8 @@ class MispObject extends AppModel
|
|||
'includeCorrelations' => !empty($filters['includeCorrelations']) ? $filters['includeCorrelations'] : 0,
|
||||
'includeDecayScore' => !empty($filters['includeDecayScore']) ? $filters['includeDecayScore'] : 0,
|
||||
'includeFullModel' => !empty($filters['includeFullModel']) ? $filters['includeFullModel'] : 0,
|
||||
'allow_proposal_blocking' => !empty($filters['allow_proposal_blocking']) ? $filters['allow_proposal_blocking'] : 0
|
||||
'allow_proposal_blocking' => !empty($filters['allow_proposal_blocking']) ? $filters['allow_proposal_blocking'] : 0,
|
||||
'metadata' => !empty($filters['metadata']) ? $filters['metadata'] : 0,
|
||||
);
|
||||
if (!empty($filters['attackGalaxy'])) {
|
||||
$params['attackGalaxy'] = $filters['attackGalaxy'];
|
||||
|
@ -1377,6 +1388,9 @@ class MispObject extends AppModel
|
|||
if (!empty($filters['score'])) {
|
||||
$params['score'] = $filters['score'];
|
||||
}
|
||||
if (!empty($filters['metadata'])) {
|
||||
$params['metadata'] = $filters['metadata'];
|
||||
}
|
||||
if ($paramsOnly) {
|
||||
return $params;
|
||||
}
|
||||
|
@ -1423,8 +1437,16 @@ class MispObject extends AppModel
|
|||
{
|
||||
$continue = true;
|
||||
while ($continue) {
|
||||
$temp = '';
|
||||
$this->Whitelist = ClassRegistry::init('Whitelist');
|
||||
$results = $this->fetchObjects($user, $params, $continue);
|
||||
if (empty($results)) {
|
||||
$loop = false;
|
||||
return true;
|
||||
}
|
||||
if ($elementCounter !== 0 && !empty($results)) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
}
|
||||
if ($params['includeSightingdb']) {
|
||||
$this->Sightingdb = ClassRegistry::init('Sightingdb');
|
||||
$results = $this->Sightingdb->attachToObjects($results, $user);
|
||||
|
@ -1433,7 +1455,6 @@ class MispObject extends AppModel
|
|||
$results = $this->Whitelist->removeWhitelistedFromArray($results, true);
|
||||
$results = array_values($results);
|
||||
$i = 0;
|
||||
$temp = '';
|
||||
foreach ($results as $object) {
|
||||
$elementCounter++;
|
||||
$handlerResult = $exportTool->handler($object, $exportToolParams);
|
||||
|
@ -1448,9 +1469,6 @@ class MispObject extends AppModel
|
|||
if (!$loop) {
|
||||
$continue = false;
|
||||
}
|
||||
if ($continue) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
}
|
||||
fwrite($tmpfile, $temp);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -84,7 +84,8 @@ class Role extends AppModel
|
|||
'id' => 'RolePermAuth',
|
||||
'text' => 'Auth key access',
|
||||
'readonlyenabled' => true,
|
||||
'title' => 'Users with this permission have access to authenticating via their Auth keys, granting them access to the API.'
|
||||
'title' => 'Users with this permission have access to authenticating via their Auth keys, granting them access to the API.',
|
||||
'site_admin_optional' => true
|
||||
),
|
||||
'perm_regexp_access' => array(
|
||||
'id' => 'RolePermRegexpAccess',
|
||||
|
|
|
@ -1261,6 +1261,54 @@ class Server extends AppModel
|
|||
'type' => 'boolean',
|
||||
'null' => true
|
||||
),
|
||||
'email_otp_enabled' => array(
|
||||
'level'=> 2,
|
||||
'description' => __('Enable two step authentication with a OTP sent by email. Requires e-mailing to be enabled. Warning: You cannot use it in combination with external authentication plugins.'),
|
||||
'value' => false,
|
||||
'errorMessage' => '',
|
||||
'test' => 'testBool',
|
||||
'beforeHook' => 'otpBeforeHook',
|
||||
'type' => 'boolean',
|
||||
'null' => true
|
||||
),
|
||||
'email_otp_length' => array (
|
||||
'level' => 2,
|
||||
'description' => __('Define the length of the OTP code sent by email'),
|
||||
'value' => '6',
|
||||
'errorMessage' => '',
|
||||
'type' => 'numeric',
|
||||
'test' => 'testForNumeric',
|
||||
'null' => true,
|
||||
),
|
||||
'email_otp_validity' => array (
|
||||
'level' => 2,
|
||||
'description' => __('Define the validity (in minutes) of the OTP code sent by email'),
|
||||
'value' => '5',
|
||||
'errorMessage' => '',
|
||||
'type' => 'numeric',
|
||||
'test' => 'testForNumeric',
|
||||
'null' => true,
|
||||
),
|
||||
'email_otp_text' => array(
|
||||
'level' => 2,
|
||||
'bigField' => true,
|
||||
'description' => __('The message sent to the user when a new OTP is requested. Use \\n for line-breaks. The following variables will be automatically replaced in the text: $otp = the new OTP generated by MISP, $username = the user\'s e-mail address, $org the Organisation managing the instance, $misp = the url of this instance, $contact = the e-mail address used to contact the support team (as set in MISP.contact), $ip the IP used to complete the first step of the login and $validity the validity time in minutes.'),
|
||||
'value' => 'Dear MISP user,\n\nYou have attempted to login to MISP ($misp) from $ip with username $username.\n\n Use the following OTP to log into MISP: $otp\n This code is valid for the next $validity minutes.\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team',
|
||||
'errorMessage' => '',
|
||||
'test' => 'testForEmpty',
|
||||
'type' => 'string',
|
||||
'null' => true,
|
||||
),
|
||||
'email_otp_exceptions' => array(
|
||||
'level' => 2,
|
||||
'bigField' => true,
|
||||
'description' => __('A comma separated list of emails for which the OTP is disabled. Note that if you remove someone from this list, the OTP will only be asked at next login.'),
|
||||
'value' => '',
|
||||
'errorMessage' => '',
|
||||
'test' => 'testForEmpty',
|
||||
'type' => 'string',
|
||||
'null' => true,
|
||||
),
|
||||
'allow_self_registration' => array(
|
||||
'level' => 1,
|
||||
'description' => __('Enabling this setting will allow users to have access to the pre-auth registration form. This will create an inbox entry for administrators to review.'),
|
||||
|
@ -3541,7 +3589,7 @@ class Server extends AppModel
|
|||
if ($errorMessage) {
|
||||
return $errorMessage;
|
||||
}
|
||||
return 'Value is not a boolean, make sure that you convert \'true\' to true for example.';
|
||||
return __('Value is not a boolean, make sure that you convert \'true\' to true for example.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3731,6 +3779,14 @@ class Server extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
public function otpBeforeHook($setting, $value)
|
||||
{
|
||||
if ($value && !empty(Configure::read('MISP.disable_emailing'))) {
|
||||
return __('Emailing is currently disabled. Enabling OTP without e-mailing being configured would lock all users out.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function testForRPZSerial($value)
|
||||
{
|
||||
if ($this->testForEmpty($value) !== true) {
|
||||
|
@ -4920,7 +4976,7 @@ class Server extends AppModel
|
|||
public function stixDiagnostics(&$diagnostic_errors, &$stixVersion, &$cyboxVersion, &$mixboxVersion, &$maecVersion, &$stix2Version, &$pymispVersion)
|
||||
{
|
||||
$result = array();
|
||||
$expected = array('stix' => '>1.2.0.6', 'cybox' => '>2.1.0.18.dev0', 'mixbox' => '1.0.3', 'maec' => '>4.1.0.14', 'stix2' => '>1.2.0', 'pymisp' => '>2.4.120');
|
||||
$expected = array('stix' => '>1.2.0.6', 'cybox' => '>2.1.0.21', 'mixbox' => '1.0.3', 'maec' => '>4.1.0.14', 'stix2' => '>1.2.0', 'pymisp' => '>2.4.120');
|
||||
// check if the STIX and Cybox libraries are working using the test script stixtest.py
|
||||
$scriptResult = shell_exec($this->getPythonVersion() . ' ' . APP . 'files' . DS . 'scripts' . DS . 'stixtest.py');
|
||||
$scriptResult = json_decode($scriptResult, true);
|
||||
|
|
|
@ -54,6 +54,10 @@ class SharingGroup extends AppModel
|
|||
);
|
||||
|
||||
private $__sgoCache = array();
|
||||
private $__sgAuthorisationCache = array(
|
||||
'save' => array(),
|
||||
'access' => array()
|
||||
);
|
||||
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
|
@ -353,6 +357,9 @@ class SharingGroup extends AppModel
|
|||
// returns true if the SG exists and the user is allowed to see it
|
||||
public function checkIfAuthorised($user, $id, $adminCheck = true)
|
||||
{
|
||||
if (isset($this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id])) {
|
||||
return $this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id];
|
||||
}
|
||||
if (Validation::uuid($id)) {
|
||||
$sgid = $this->SharingGroup->find('first', array(
|
||||
'conditions' => array('SharingGroup.uuid' => $id),
|
||||
|
@ -372,8 +379,10 @@ class SharingGroup extends AppModel
|
|||
return false;
|
||||
}
|
||||
if (($adminCheck && $user['Role']['perm_site_admin']) || $this->SharingGroupServer->checkIfAuthorised($id) || $this->SharingGroupOrg->checkIfAuthorised($id, $user['org_id'])) {
|
||||
$this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id] = true;
|
||||
return true;
|
||||
}
|
||||
$this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -485,7 +494,7 @@ class SharingGroup extends AppModel
|
|||
return $results;
|
||||
}
|
||||
|
||||
public function captureSG($sg, $user)
|
||||
public function captureSG($sg, $user, $syncLocal=false)
|
||||
{
|
||||
$existingSG = !isset($sg['uuid']) ? null : $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
|
@ -501,6 +510,34 @@ class SharingGroup extends AppModel
|
|||
if (!$user['Role']['perm_sharing_group']) {
|
||||
return false;
|
||||
}
|
||||
// check if current user is contained in the SG and we are in a local sync setup
|
||||
if (!empty($sg['uuid'])) {
|
||||
if (isset($this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']])) {
|
||||
$authorisedToSave = $this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']];
|
||||
} else {
|
||||
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
|
||||
$this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']] = $authorisedToSave;
|
||||
}
|
||||
} else {
|
||||
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
|
||||
}
|
||||
if (!$user['Role']['perm_site_admin'] &&
|
||||
!($user['Role']['perm_sync'] && $syncLocal ) &&
|
||||
!$authorisedToSave
|
||||
) {
|
||||
$this->Log->create();
|
||||
$entry = array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'SharingGroup',
|
||||
'model_id' => $sg['SharingGroup']['uuid'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'error',
|
||||
'user_id' => $user['id'],
|
||||
'title' => 'Tried to save a sharing group but the user does not belong to it.'
|
||||
);
|
||||
$this->Log->save($entry);
|
||||
return false;
|
||||
}
|
||||
$this->create();
|
||||
$newSG = array();
|
||||
$attributes = array(
|
||||
|
|
|
@ -7,7 +7,7 @@ if (0 != count($attributes)): ?>
|
|||
<h4><?php echo __('Successfully added attributes');?>:</h4>
|
||||
<table class="table table-striped table-hover table-condensed">
|
||||
<tr>
|
||||
<th><?php echo __('Uuid');?></th>
|
||||
<th><?php echo __('UUID');?></th>
|
||||
<th><?php echo __('Category');?></th>
|
||||
<th><?php echo __('Type');?></th>
|
||||
<th><?php echo __('Value');?></th>
|
||||
|
@ -29,7 +29,7 @@ if (isset($fails)):?>
|
|||
<h4><?php echo __('Failed indicators');?>:</h4>
|
||||
<table class="table table-striped table-hover table-condensed">
|
||||
<tr>
|
||||
<th><?php echo __('Uuid');?></th>
|
||||
<th><?php echo __('UUID');?></th>
|
||||
<th><?php echo __('Search term');?></th>
|
||||
<th><?php echo __('Content');?></th>
|
||||
</tr><?php
|
||||
|
|
|
@ -301,27 +301,27 @@
|
|||
)
|
||||
)
|
||||
);
|
||||
if (!Configure::read('MISP.completely_disable_correlation') && Configure::read('MISP.allow_disabling_correlation')) {
|
||||
$table_data[] = array(
|
||||
'key' => __('Correlation'),
|
||||
'class' => $event['Event']['disable_correlation'] ? 'background-red bold' : '',
|
||||
'html' => sprintf(
|
||||
'%s%s',
|
||||
$event['Event']['disable_correlation'] ? __('Disabled') : __('Enabled'),
|
||||
(!$mayModify && !$isSiteAdmin) ? '' : sprintf(
|
||||
}
|
||||
if (!Configure::read('MISP.completely_disable_correlation') && Configure::read('MISP.allow_disabling_correlation')) {
|
||||
$table_data[] = array(
|
||||
'key' => __('Correlation'),
|
||||
'class' => $event['Event']['disable_correlation'] ? 'background-red bold' : '',
|
||||
'html' => sprintf(
|
||||
'%s%s',
|
||||
$event['Event']['disable_correlation'] ? __('Disabled') : __('Enabled'),
|
||||
(!$mayModify && !$isSiteAdmin) ? '' : sprintf(
|
||||
sprintf(
|
||||
' (<a onClick="getPopup(%s);" style="%scursor:pointer;font-weight:normal;">%s</a>)',
|
||||
sprintf(
|
||||
' (<a onClick="getPopup(%s);" style="%scursor:pointer;font-weight:normal;">%s</a>)',
|
||||
sprintf(
|
||||
"'%s', 'events', 'toggleCorrelation', '', '#confirmation_box'",
|
||||
h($event['Event']['id'])
|
||||
),
|
||||
$event['Event']['disable_correlation'] ? 'color:white;' : '',
|
||||
$event['Event']['disable_correlation'] ? __('enable') : __('disable')
|
||||
)
|
||||
"'%s', 'events', 'toggleCorrelation', '', '#confirmation_box'",
|
||||
h($event['Event']['id'])
|
||||
),
|
||||
$event['Event']['disable_correlation'] ? 'color:white;' : '',
|
||||
$event['Event']['disable_correlation'] ? __('enable') : __('disable')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<div class="span8">
|
||||
<h2><?php echo nl2br(h($title)); ?></h2>
|
||||
<dl>
|
||||
<dt><?php echo __('Uuid');?></dt>
|
||||
<dt><?php echo __('UUID');?></dt>
|
||||
<dd><?php echo h($event['Event']['uuid']); ?></dd>
|
||||
<dt><?php echo Configure::read('MISP.showorgalternate') ? __('Source Organisation') : __('Org')?></dt>
|
||||
<dd><?php echo h($event['Orgc']['name']); ?></dd>
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
<th><?php echo __('Logo');?></th>
|
||||
<th><?php echo $this->Paginator->sort('name');?></th>
|
||||
<?php if ($isSiteAdmin): ?>
|
||||
<th><?php echo $this->Paginator->sort('uuid');?></th>
|
||||
<th><?php echo $this->Paginator->sort('uuid', 'UUID');?></th>
|
||||
<?php endif; ?>
|
||||
<th><?php echo $this->Paginator->sort('description');?></th>
|
||||
<th><?php echo $this->Paginator->sort('nationality');?></th>
|
||||
|
|
|
@ -201,7 +201,7 @@ Templates are devided into sections, with each section having a title and a desc
|
|||
<b><?php echo __('General Event Information');?></b>
|
||||
<ul>
|
||||
<li><b><?php echo __('ID');?>:</b> <?php echo __('The ID of the event.');?></li>
|
||||
<li><b><?php echo __('Uuid');?>:</b> <?php echo __('In order to avoid collisions between events and attributes (during for example a sync) a Uuid is assigned that uniquely identifies each of them.');?></li>
|
||||
<li><b><?php echo __('UUID');?>:</b> <?php echo __('In order to avoid collisions between events and attributes (during for example a sync) a Uuid is assigned that uniquely identifies each of them.');?></li>
|
||||
<li><b><?php echo __('Org');?></b> <?php echo __('The organisation that has originally created the event. The logo (if it exists on the server, alternatively a string) representing the organisation is also shown int he right upper corner.');?></li>
|
||||
<li><b><?php echo __('Contributors');?>:</b> <?php echo __('Shows a list of the organisations that have contributed to the event via proposals. If you click any of the logos listed here, you\'ll get redirected to a filtered event history view, including only the changes made by the organisation.');?></li>
|
||||
<li><b><?php echo __('Tags');?>:</b> <?php echo __('A list of tags associated with the event. Clicking a tag will show a list of events with the same tag attached. The little cross next to each tag allows you to remove the tag from the event, whilst the \'+\' button allows you to assign a tag. For the latter two options to be visible, you have to have tagging permission.');?></li>
|
||||
|
|
|
@ -41,7 +41,11 @@
|
|||
<?php
|
||||
echo $this->Form->input($k, array(
|
||||
'type' => 'checkbox',
|
||||
'class' => 'checkbox ' . ($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
|
||||
'class' => sprintf(
|
||||
'checkbox %s %s',
|
||||
($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
|
||||
empty($flag['site_admin_optional']) ? 'site_admin_enforced' : 'site_admin_optional'
|
||||
),
|
||||
'checked' => false,
|
||||
'label' => Inflector::humanize(substr($k, 5))
|
||||
));
|
||||
|
|
|
@ -41,7 +41,11 @@
|
|||
<?php
|
||||
echo $this->Form->input($k, array(
|
||||
'type' => 'checkbox',
|
||||
'class' => 'checkbox ' . ($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
|
||||
'class' => sprintf(
|
||||
'checkbox %s %s',
|
||||
($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
|
||||
empty($flag['site_admin_optional']) ? 'site_admin_enforced' : 'site_admin_optional'
|
||||
),
|
||||
'label' => Inflector::humanize(substr($k, 5))
|
||||
));
|
||||
if ($counter%3 == 0) echo "<div class = 'input clear'></div>";
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<input type="text" id="ServerExternalName" <?php if (isset($this->request->data['Server']['external_name'])) echo 'value="' . $this->request->data['Server']['external_name'] . '"';?>>
|
||||
</div>
|
||||
<div id="ServerExternalUuidContainer" class="input select hiddenField" style="display:none;">
|
||||
<label for="ServerExternalUuid"><?php echo __('Remote Organisation\'s Uuid');?></label>
|
||||
<label for="ServerExternalUuid"><?php echo __('Remote Organisation\'s UUID');?></label>
|
||||
<input type="text" id="ServerExternalUuid" <?php if (isset($this->request->data['Server']['external_uuid'])) echo 'value="' . $this->request->data['Server']['external_uuid'] . '"';?>>
|
||||
</div>
|
||||
<div class = "input clear"></div>
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
<input type="text" id="ServerExternalName" <?php if (isset($this->request->data['Server']['external_name'])) echo 'value="' . $this->request->data['Server']['external_name'] . '"';?>>
|
||||
</div>
|
||||
<div id="ServerExternalUuidContainer" class="input select hiddenField" style="display:none;">
|
||||
<label for="ServerExternalUuid"><?php echo __('Remote Organisation\'s Uuid');?></label>
|
||||
<label for="ServerExternalUuid"><?php echo __('Remote Organisation\'s UUID');?></label>
|
||||
<input type="text" id="ServerExternalUuid" <?php if (isset($this->request->data['Server']['external_uuid'])) echo 'value="' . $this->request->data['Server']['external_uuid'] . '"';?>>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<?php echo h($event['Event']['id']); ?>
|
||||
|
||||
</dd>
|
||||
<dt><?php echo __('Uuid');?></dt>
|
||||
<dt><?php echo __('UUID');?></dt>
|
||||
<dd>
|
||||
<?php echo h($event['Event']['uuid']); ?>
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php echo $this->Flash->render(); ?>
|
||||
|
||||
<div class="actions sideMenu">
|
||||
<div style="padding: 10px;">
|
||||
<p> <?php echo __("Your administrator has turned on an additional authentication step which
|
||||
requires you to enter a OTP (one time password) you have received via email.");?>
|
||||
</p>
|
||||
<p> <?php echo __("Make sure to check your SPAM folder.");?> </p>
|
||||
<a href='<?php echo $baseurl; ?>/users/email_otp'> <button class='btn'> <?php echo __("Resend"); ?> </button></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
echo $this->element('/genericElements/Form/genericForm', array(
|
||||
"form" => $this->Form,
|
||||
"data" => array(
|
||||
"title" => __("Validate your OTP"),
|
||||
"fields" => array(
|
||||
array(
|
||||
"field" => "otp",
|
||||
"label" => __("One Time Password"),
|
||||
"type" => "text",
|
||||
"placeholder" => __("Enter your OTP here"),
|
||||
),
|
||||
),
|
||||
"submit" => array (
|
||||
"action" => "EmailOtp",
|
||||
),
|
||||
)));
|
||||
?>
|
|
@ -35,6 +35,8 @@
|
|||
<dd><?php echo h($stats['org_count']); ?> </dd>
|
||||
<dt><?php echo __('Local Organisations'); ?></dt>
|
||||
<dd><?php echo h($stats['local_org_count']); ?> </dd>
|
||||
<dt><?php echo __('Event creator orgs'); ?></dt>
|
||||
<dd><?php echo h($stats['contributing_org_count']); ?> </dd>
|
||||
<dt><?php echo __('Average Users / Org'); ?></dt>
|
||||
<dd><?php echo h($stats['average_user_per_org']); ?> </dd>
|
||||
<dt><?php echo __('Discussion threads'); ?></dt>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c7104e8819d6b789b24a45655aa28625a8c4c346
|
||||
Subproject commit cb9bff18e8c94584588ec1333affb4959cdc08d0
|
|
@ -1 +1 @@
|
|||
Subproject commit 3b5451c32518da3e29c575e868d245f27c18dcf4
|
||||
Subproject commit 4f05fc5bcd322e5d983c7d8d7de81848f09de8ba
|
|
@ -20,8 +20,13 @@ import sys, json, os, datetime
|
|||
import pymisp
|
||||
import re
|
||||
import uuid
|
||||
from stix2 import *
|
||||
from stix2.base import STIXJSONEncoder
|
||||
from stix2.exceptions import InvalidValueError, TLPMarkingDefinitionError
|
||||
from stix2.properties import DictionaryProperty, ListProperty, StringProperty, TimestampProperty
|
||||
from stix2.v20.common import MarkingDefinition, TLP_WHITE, TLP_GREEN, TLP_AMBER, TLP_RED
|
||||
from stix2.v20.observables import WindowsPESection
|
||||
from stix2.v20.sdo import AttackPattern, CourseOfAction, CustomObject, Identity, Indicator, IntrusionSet, Malware, ObservedData, Report, ThreatActor, Tool, Vulnerability
|
||||
from stix2.v20.sro import Relationship
|
||||
from misp2stix2_mapping import *
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
|
@ -48,6 +53,7 @@ class StixBuilder():
|
|||
self.orgs = []
|
||||
self.galaxies = []
|
||||
self.ids = {}
|
||||
self.custom_objects = {}
|
||||
|
||||
def loadEvent(self, args):
|
||||
pathname = os.path.dirname(args[0])
|
||||
|
@ -468,19 +474,24 @@ class StixBuilder():
|
|||
if markings:
|
||||
markings = self.handle_tags(markings)
|
||||
custom_object_args['object_marking_refs'] = markings
|
||||
@CustomObject(custom_object_type, [('id', properties.StringProperty(required=True)),
|
||||
('labels', properties.ListProperty(labels, required=True)),
|
||||
('x_misp_value', properties.StringProperty(required=True)),
|
||||
('created', properties.TimestampProperty(required=True, precision='millisecond')),
|
||||
('modified', properties.TimestampProperty(required=True, precision='millisecond')),
|
||||
('created_by_ref', properties.StringProperty(required=True)),
|
||||
('object_marking_refs', properties.ListProperty(markings)),
|
||||
('x_misp_comment', properties.StringProperty()),
|
||||
('x_misp_category', properties.StringProperty())
|
||||
])
|
||||
class Custom(object):
|
||||
def __init__(self, **kwargs):
|
||||
return
|
||||
if custom_object_type not in self.custom_objects:
|
||||
@CustomObject(custom_object_type, [
|
||||
('id', StringProperty(required=True)),
|
||||
('labels', ListProperty(labels, required=True)),
|
||||
('x_misp_value', StringProperty(required=True)),
|
||||
('created', TimestampProperty(required=True, precision='millisecond')),
|
||||
('modified', TimestampProperty(required=True, precision='millisecond')),
|
||||
('created_by_ref', StringProperty(required=True)),
|
||||
('object_marking_refs', ListProperty(markings)),
|
||||
('x_misp_comment', StringProperty()),
|
||||
('x_misp_category', StringProperty())
|
||||
])
|
||||
class Custom(object):
|
||||
def __init__(self, **kwargs):
|
||||
return
|
||||
self.custom_objects[custom_object_type] = Custom
|
||||
else:
|
||||
Custom = self.custom_objects[custom_object_type]
|
||||
custom_object = Custom(**custom_object_args)
|
||||
self.append_object(custom_object)
|
||||
|
||||
|
@ -606,18 +617,23 @@ class StixBuilder():
|
|||
'x_misp_category': category, 'created_by_ref': self.identity_id}
|
||||
if hasattr(misp_object, 'comment') and misp_object.get('comment'):
|
||||
custom_object_args['x_misp_comment'] = misp_object['comment']
|
||||
@CustomObject(custom_object_type, [('id', properties.StringProperty(required=True)),
|
||||
('labels', properties.ListProperty(labels, required=True)),
|
||||
('x_misp_values', properties.DictionaryProperty(required=True)),
|
||||
('created', properties.TimestampProperty(required=True, precision='millisecond')),
|
||||
('modified', properties.TimestampProperty(required=True, precision='millisecond')),
|
||||
('created_by_ref', properties.StringProperty(required=True)),
|
||||
('x_misp_comment', properties.StringProperty()),
|
||||
('x_misp_category', properties.StringProperty())
|
||||
])
|
||||
class Custom(object):
|
||||
def __init__(self, **kwargs):
|
||||
return
|
||||
if custom_object_type not in self.custom_objects:
|
||||
@CustomObject(custom_object_type, [
|
||||
('id', StringProperty(required=True)),
|
||||
('labels', ListProperty(labels, required=True)),
|
||||
('x_misp_value', StringProperty(required=True)),
|
||||
('created', TimestampProperty(required=True, precision='millisecond')),
|
||||
('modified', TimestampProperty(required=True, precision='millisecond')),
|
||||
('created_by_ref', StringProperty(required=True)),
|
||||
('x_misp_comment', StringProperty()),
|
||||
('x_misp_category', StringProperty())
|
||||
])
|
||||
class Custom(object):
|
||||
def __init__(self, **kwargs):
|
||||
return
|
||||
self.custom_objects[custom_object_type] = Custom
|
||||
else:
|
||||
Custom = self.custom_objects[custom_object_type]
|
||||
custom_object = Custom(**custom_object_args)
|
||||
self.append_object(custom_object)
|
||||
|
||||
|
@ -659,7 +675,7 @@ class StixBuilder():
|
|||
observed_data_args.update(self.handle_time_fields(misp_object, timestamp, 'observed-data'))
|
||||
try:
|
||||
observed_data = ObservedData(**observed_data_args)
|
||||
except exceptions.InvalidValueError:
|
||||
except InvalidValueError:
|
||||
observed_data = self.fix_enumeration_issues(name, observed_data_args)
|
||||
self.append_object(observed_data)
|
||||
|
||||
|
@ -737,7 +753,7 @@ class StixBuilder():
|
|||
'definition': {definition_type: definition}}
|
||||
try:
|
||||
self.markings[tag] = MarkingDefinition(**marking_definition)
|
||||
except exceptions.TLPMarkingDefinitionError:
|
||||
except (TLPMarkingDefinitionError, ValueError):
|
||||
return
|
||||
return marking_id
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 28687d90d575332776480cd5d683361e7485033c
|
||||
Subproject commit 2c116cbd1ff50b2ed677ea6af08c8a6bf511a6fd
|
|
@ -59,6 +59,15 @@
|
|||
box-shadow: 0 0 20px rgba(82, 168, 236, 1);`
|
||||
}
|
||||
|
||||
.vis-item.sighting_positive {
|
||||
background-color: green;
|
||||
border-color: white;
|
||||
}
|
||||
.vis-item.sighting_negative {
|
||||
background-color: red;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.vis-item.object {
|
||||
background-color: #3465a4;
|
||||
border-color: black;
|
||||
|
|
|
@ -29,10 +29,16 @@ var options = {
|
|||
return build_object_template(item);
|
||||
|
||||
case "object_attribute":
|
||||
console.log('Error');
|
||||
console.log('Error: Group not valid');
|
||||
break;
|
||||
|
||||
default:
|
||||
if (item.className == "sighting_positive" || item.className == "sighting_negative") {
|
||||
return build_sighting_template(item);
|
||||
} else {
|
||||
console.log(item)
|
||||
console.log('Error: Unkown group');
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -199,6 +205,13 @@ function build_object_template(obj) {
|
|||
return html;
|
||||
}
|
||||
|
||||
function build_sighting_template(attr){
|
||||
var span = $('<span data-itemID="'+attr.id+'">');
|
||||
span.text(attr.content);
|
||||
var html = span[0].outerHTML;
|
||||
return html;
|
||||
}
|
||||
|
||||
function contain_seen_attribute(obj) {
|
||||
if (obj['Attribute'] === undefined) {
|
||||
return false;
|
||||
|
@ -372,6 +385,8 @@ function map_scope(val) {
|
|||
return 'seen';
|
||||
case 'Object relationship':
|
||||
return 'relationship';
|
||||
case 'Sightings':
|
||||
return 'sightings';
|
||||
default:
|
||||
return 'seen';
|
||||
}
|
||||
|
@ -400,7 +415,8 @@ function update_badge() {
|
|||
|
||||
function reload_timeline() {
|
||||
update_badge();
|
||||
var payload = {scope: map_scope($('#select_timeline_scope').val())};
|
||||
var selectedScope = map_scope($('#select_timeline_scope').val());
|
||||
var payload = {scope: selectedScope};
|
||||
$.ajax({
|
||||
url: "/events/"+"getEventTimeline"+"/"+scope_id+"/"+extended_text+"event.json",
|
||||
dataType: 'json',
|
||||
|
@ -413,6 +429,8 @@ function reload_timeline() {
|
|||
},
|
||||
success: function( data, textStatus, jQxhr ){
|
||||
items_timeline.clear();
|
||||
mapping_text_to_id = new Map();
|
||||
var itemIds = {};
|
||||
for (var item of data.items) {
|
||||
item.className = item.group;
|
||||
item.orig_id = item.id;
|
||||
|
@ -420,16 +438,45 @@ function reload_timeline() {
|
|||
set_spanned_time(item);
|
||||
if (item.group == 'object') {
|
||||
for (var attr of item.Attribute) {
|
||||
mapping_text_to_id.set(attr.contentType+': '+attr.content+' ('+item.orig_id+')', item.id);
|
||||
if (selectedScope == 'sightings') {
|
||||
var k = attr.contentType+': '+attr.content+' ('+item.orig_id.split('-')[0]+')'
|
||||
if (!mapping_text_to_id.get(k)) {
|
||||
mapping_text_to_id.set(k, item.id);
|
||||
}
|
||||
} else {
|
||||
mapping_text_to_id.set(attr.contentType+': '+attr.content+' ('+item.orig_id+')', item.id);
|
||||
}
|
||||
adjust_text_length(attr);
|
||||
}
|
||||
} else {
|
||||
mapping_text_to_id.set(item.content+' ('+item.orig_id+')', item.id);
|
||||
if (selectedScope == 'sightings') {
|
||||
var k = item.content+' ('+item.orig_id.split('-')[0]+')'
|
||||
if (!mapping_text_to_id.get(k)) {
|
||||
mapping_text_to_id.set(k, item.id);
|
||||
}
|
||||
} else {
|
||||
mapping_text_to_id.set(item.content+' ('+item.orig_id+')', item.id);
|
||||
}
|
||||
adjust_text_length(item);
|
||||
}
|
||||
itemIds[item.attribute_id] = item.content;
|
||||
if (selectedScope == 'sightings') {
|
||||
item.group = item.attribute_id;
|
||||
item.content = '';
|
||||
}
|
||||
}
|
||||
items_timeline.add(data.items);
|
||||
handle_not_seen_enabled($('#checkbox_timeline_display_hide_not_seen_enabled').prop('checked'), false)
|
||||
if (selectedScope == 'sightings') {
|
||||
var groups = Object.keys(itemIds).map(function(id) {
|
||||
return {id: id, content: itemIds[id]}
|
||||
})
|
||||
eventTimeline.setGroups(groups);
|
||||
eventTimeline.setOptions({selectable: false});
|
||||
} else {
|
||||
eventTimeline.setOptions({selectable: true});
|
||||
eventTimeline.setGroups([]);
|
||||
}
|
||||
},
|
||||
error: function( jqXhr, textStatus, errorThrown ){
|
||||
console.log( errorThrown );
|
||||
|
@ -610,11 +657,11 @@ function init_popover() {
|
|||
label: "Scope",
|
||||
tooltip: "The time scope represented by the timeline",
|
||||
event: function(value) {
|
||||
if (value == "First seen/Last seen") {
|
||||
if (value == "First seen/Last seen" || value == "Sightings") {
|
||||
reload_timeline();
|
||||
}
|
||||
},
|
||||
options: ["First seen/Last seen"],
|
||||
options: ["First seen/Last seen", "Sightings"],
|
||||
default: "First seen/Last seen"
|
||||
});
|
||||
|
||||
|
|
|
@ -4215,7 +4215,7 @@ function checkRolePerms() {
|
|||
$('.permFlags').show();
|
||||
}
|
||||
if ($("#RolePermSiteAdmin").prop('checked')) {
|
||||
$('.checkbox').prop('checked', true);
|
||||
$('.site_admin_enforced').prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
#!/bin/bash
|
||||
if [ ! -d ./app/Lib/cakephp/app ]
|
||||
then
|
||||
echo "CakePHP has not been pulled."
|
||||
echo "Make sure all submodules are intialized and updated. Please run:"
|
||||
echo "git submodule init"
|
||||
echo "git submodule update"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dpkg-buildpackage -b -rfakeroot -us -uc
|
||||
|
||||
|
|
|
@ -6567,5 +6567,5 @@
|
|||
"id"
|
||||
]
|
||||
},
|
||||
"db_version": "51"
|
||||
}
|
||||
"db_version": "53"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
misp (2.4.125-1) UNRELEASED; urgency=low
|
||||
|
||||
* Bump to release 2.4.125
|
||||
|
||||
-- Sebastien Tricaud <sebastien.tricaud@devo.com> Fri, 1 May 2020 17:32:05 -0800
|
||||
|
||||
misp (2.4.123-1) UNRELEASED; urgency=low
|
||||
|
||||
* Bump to release 2.4.123
|
||||
|
|
|
@ -6,3 +6,4 @@ cti-python-stix2 usr/share/misp
|
|||
PyMISP usr/share/misp
|
||||
INSTALL/MYSQL.sql usr/share/doc/misp
|
||||
INSTALL/misp-workers.service usr/share/doc/misp
|
||||
INSTALL/setup/config.php usr/share/misp/app/Plugin/CakeResque/Config
|
||||
|
|
|
@ -36,8 +36,6 @@ if [ "$1" = "configure" ] ; then
|
|||
|
||||
a2dissite 000-default || true
|
||||
a2ensite misp.apache2 || true
|
||||
a2enmod rewrite
|
||||
a2enmod headers
|
||||
|
||||
db_get misp/mariadb_host
|
||||
HOST=$RET
|
||||
|
@ -53,8 +51,8 @@ if [ "$1" = "configure" ] ; then
|
|||
BASEURL=$RET
|
||||
db_stop
|
||||
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "CREATE USER IF NOT EXISTS '$MISPDBUSER'@'localhost' IDENTIFIED BY '$MISPDBUSERPWD';"
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "GRANT ALL PRIVILEGES ON misp.* TO '$MISPDBUSER'@'localhost';"
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "CREATE USER IF NOT EXISTS '$MISPDBUSER'@'%' IDENTIFIED BY '$MISPDBUSERPWD';"
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "GRANT ALL PRIVILEGES ON misp.* TO '$MISPDBUSER'@'%';"
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "FLUSH PRIVILEGES;"
|
||||
echo "Creating MISP Database..."
|
||||
mysql -h$HOST -uroot -p$ROOTPWD -e "CREATE DATABASE $MISPDB;" && gunzip < /usr/share/doc/misp/MYSQL.sql.gz | mysql -h$HOST -u$MISPDBUSER -p$MISPDBUSERPWD $MISPDB || true
|
||||
|
@ -80,7 +78,7 @@ if [ "$1" = "configure" ] ; then
|
|||
chmod +x /usr/share/misp/app/Console/worker/start.sh
|
||||
cat /usr/share/doc/misp/misp-workers.service > /etc/systemd/system/misp-workers.service
|
||||
sed -i -E "s/\/var\/www\/MISP/\/usr\/share\/misp/" /etc/systemd/system/misp-workers.service
|
||||
# systemctl daemon-reload
|
||||
|
||||
# systemctl enable --now misp-workers
|
||||
fi
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
a2enmod rewrite || true
|
||||
a2enmod headers || true
|
|
@ -20,7 +20,7 @@ bash /tmp/INSTALL.sh -c
|
|||
### 0/ MISP Ubuntu 18.04-server install - status
|
||||
-------------------------
|
||||
!!! notice
|
||||
Installer tested working by [@SteveClement](https://twitter.com/SteveClement) on 20190513 (works with **Ubuntu 18.10/19.04** too)
|
||||
Installer tested working by [@SteveClement](https://twitter.com/SteveClement) on 20200501 (works with **Ubuntu 18.10/19.04** too)
|
||||
|
||||
!!! notice
|
||||
This document also serves as a source for the [INSTALL-misp.sh](https://github.com/MISP/MISP/blob/2.4/INSTALL/INSTALL.sh) script.
|
||||
|
|
|
@ -20,7 +20,7 @@ bash /tmp/INSTALL.sh -c
|
|||
### 0/ MISP Ubuntu 20.04-server install - status
|
||||
-------------------------
|
||||
!!! notice
|
||||
Installer tested working by [@SteveClement](https://twitter.com/SteveClement) on 20200427
|
||||
Installer tested working by [@SteveClement](https://twitter.com/SteveClement) on 20200501
|
||||
|
||||
!!! notice
|
||||
This document also serves as a source for the [INSTALL-misp.sh](https://github.com/MISP/MISP/blob/2.4/INSTALL/INSTALL.sh) script.
|
||||
|
|
|
@ -9,8 +9,8 @@ mail2misp () {
|
|||
cd /usr/local/src/
|
||||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
$SUDO_CMD git clone https://github.com/MISP/mail_to_misp.git
|
||||
$SUDO_CMD git clone git://github.com/stricaud/faup.git faup
|
||||
$SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca
|
||||
[[ ! -d "faup" ]] && $SUDO_CMD git clone git://github.com/stricaud/faup.git faup
|
||||
[[ ! -d "gtcaca" ]] && $SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup mail_to_misp gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
|
|
|
@ -5,10 +5,27 @@
|
|||
# Main MISP Modules install function
|
||||
mispmodules () {
|
||||
cd /usr/local/src/
|
||||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
## TODO: checkUsrLocalSrc in main doc
|
||||
debug "Cloning misp-modules"
|
||||
$SUDO_CMD git clone https://github.com/MISP/misp-modules.git
|
||||
cd misp-modules
|
||||
$SUDO_CMD git clone git://github.com/stricaud/gtcaca.git
|
||||
$SUDO_CMD git clone git://github.com/stricaud/faup.git
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
|
||||
# Install gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
cd build
|
||||
$SUDO_CMD cmake .. && $SUDO_CMD make
|
||||
sudo make install
|
||||
cd ../../faup
|
||||
# Install faup
|
||||
$SUDO_CMD mkdir -p build
|
||||
cd build
|
||||
$SUDO_CMD cmake .. && $SUDO_CMD make
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ../../misp-modules
|
||||
# some misp-modules dependencies
|
||||
sudo apt install libpq5 libjpeg-dev tesseract-ocr libpoppler-cpp-dev imagemagick libopencv-dev zbar-tools libzbar0 libzbar-dev libfuzzy-dev -y
|
||||
# If you build an egg, the user you build it as need write permissions in the CWD
|
||||
|
|
|
@ -217,18 +217,19 @@ EOF
|
|||
checkInstaller () {
|
||||
# Workaround: shasum is not available on RHEL, only checking sha512
|
||||
if [[ $FLAVOUR == "rhel" ]] || [[ $FLAVOUR == "centos" ]]; then
|
||||
INSTsum=$(sha512sum ${0} | cut -f1 -d\ )
|
||||
/usr/bin/wget --no-cache -q -O /tmp/INSTALL.sh.sha512 https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh.sha512
|
||||
INSTsum=$(sha512sum ${0} | cut -f1 -d\ )
|
||||
/usr/bin/wget --no-cache -q -O /tmp/INSTALL.sh.sha512 https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh.sha512
|
||||
chsum=$(cat /tmp/INSTALL.sh.sha512)
|
||||
if [[ "${chsum}" == "${INSTsum}" ]]; then
|
||||
echo "SHA512 matches"
|
||||
else
|
||||
echo "SHA512: ${chsum} does not match the installer sum of: ${INSTsum}"
|
||||
# exit 1 # uncomment when/if PR is merged
|
||||
fi
|
||||
if [[ "${chsum}" == "${INSTsum}" ]]; then
|
||||
echo "SHA512 matches"
|
||||
else
|
||||
echo "SHA512: ${chsum} does not match the installer sum of: ${INSTsum}"
|
||||
# exit 1 # uncomment when/if PR is merged
|
||||
fi
|
||||
else
|
||||
# TODO: Implement $FLAVOUR checks and install depending on the platform we are on
|
||||
if [[ $(which shasum > /dev/null 2>&1 ; echo $?) != 0 ]]; then
|
||||
sudo apt update
|
||||
sudo apt install libdigest-sha-perl -qyy
|
||||
fi
|
||||
# SHAsums to be computed, not the -- notatiation is for ease of use with rhash
|
||||
|
@ -512,8 +513,7 @@ setBaseURL () {
|
|||
CONN=$(ip -br -o -4 a |grep UP |head -1 |tr -d "UP")
|
||||
IFACE=`echo $CONN |awk {'print $1'}`
|
||||
IP=`echo $CONN |awk {'print $2'}| cut -f1 -d/`
|
||||
# TODO: Consider "QEMU"
|
||||
if [[ "$(checkManufacturer)" != "innotek GmbH" ]] && [[ "$(checkManufacturer)" != "VMware, Inc." ]]; then
|
||||
if [[ "$(checkManufacturer)" != "innotek GmbH" ]] && [[ "$(checkManufacturer)" != "VMware, Inc." ]] && [[ "$(checkManufacturer)" != "QEMU" ]]; then
|
||||
debug "We guess that this is a physical machine and cannot possibly guess what the MISP_BASEURL might be."
|
||||
if [[ "$UNATTENDED" != "1" ]]; then
|
||||
echo "You can now enter your own MISP_BASEURL, if you wish to NOT do that, the MISP_BASEURL will be empty, which will work, but ideally you configure it afterwards."
|
||||
|
@ -541,12 +541,20 @@ setBaseURL () {
|
|||
MISP_BASEURL="https://misp.local"
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
else
|
||||
elif [[ "$(checkManufacturer)" == "innotek GmbH" ]]; then
|
||||
MISP_BASEURL='https://localhost:8443'
|
||||
IP=$(ip addr show | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}' |grep -v "127.0.0.1" |tail -1)
|
||||
sudo iptables -t nat -A OUTPUT -p tcp --dport 8443 -j DNAT --to ${IP}:443
|
||||
# Webserver configuration
|
||||
FQDN='localhost.localdomain'
|
||||
elif [[ "$(checkManufacturer)" == "VMware, Inc." ]]; then
|
||||
MISP_BASEURL='""'
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
else
|
||||
MISP_BASEURL='""'
|
||||
# Webserver configuration
|
||||
FQDN='misp.local'
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
a2enmod rewrite || true
|
||||
a2enmod headers || true
|
|
@ -0,0 +1,207 @@
|
|||
#!/usr/bin/env perl
|
||||
#
|
||||
# Author: Sebastien Tricaud <sebastien@honeynet.org>
|
||||
# This script is written in Perl because it is default to most distributions
|
||||
# without any dependency.
|
||||
#
|
||||
# MISP Configuration is handled mostly via its UI. This script helps to configure
|
||||
# MISP for anything that can be configured. For now this is mostly CakePHP stuff.
|
||||
#
|
||||
# How to use?
|
||||
# 1) dump the configuration options that are already set:
|
||||
# misp-config --create
|
||||
# -> Will dump in /etc/misp/misp.conf.d/ unless to add a --prefix option, such as:
|
||||
# misp-config --misp-path /var/www/MISP --prefix /var/www/MISP/etc/ --create
|
||||
#
|
||||
# 2) set the configuration manually from /etc/misp/misp.conf.d/ with your favorite text editor (emacs)
|
||||
# then load to MISP using:
|
||||
# misp-config --apply
|
||||
# Of course, if your MISP instance is installed in /var/www/MISP you should do:
|
||||
# misp-config --misp-path /var/www/MISP --apply
|
||||
#
|
||||
# Note there is a configuration, Internal.conf, which where CakePHP has no control, for example
|
||||
# it will change the source code where there was a lack of configuration capability.
|
||||
#
|
||||
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Path qw(make_path);
|
||||
|
||||
my $misp_user="www-data";
|
||||
my $misp_path="/usr/share/misp";
|
||||
my $misp_config_path="/etc/misp/";
|
||||
my $prefix="";
|
||||
my $create=0;
|
||||
my $apply=0;
|
||||
my %CATEGORIES_CREATED = ();
|
||||
|
||||
if ($#ARGV < 0) {
|
||||
print "\nUsage: misp-config --user www-data
|
||||
\t --misp-path /usr/share/misp
|
||||
\t --prefix /usr/local
|
||||
\t --apply: Applies configuration from $misp_config_path
|
||||
\t --create: Create configuration from all available options\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub replace_inplace_content {
|
||||
my ($filename, $buftoreplace, $replacement) = @_;
|
||||
|
||||
open my $fp, "<:encoding(UTF-8)", $filename or die "unable to open $filename!";
|
||||
local $/ = undef;
|
||||
my $data = <$fp>;
|
||||
close $fp;
|
||||
|
||||
$data =~ s/$buftoreplace/$replacement/g;
|
||||
|
||||
open $fp, ">:encoding(UTF-8)", $filename or die "Could not write to '$filename'";
|
||||
print $fp $data;
|
||||
close $fp;
|
||||
}
|
||||
|
||||
while (my ($i, $arg) = each @ARGV) {
|
||||
if ($arg eq "--user") {
|
||||
$misp_user = $ARGV[$i+1];
|
||||
}
|
||||
if ($arg eq "--misp-path") {
|
||||
$misp_path = $ARGV[$i+1];
|
||||
}
|
||||
if ($arg eq "--prefix") {
|
||||
$prefix = $ARGV[$i+1];
|
||||
$misp_config_path = "$prefix/$misp_config_path"
|
||||
}
|
||||
if ($arg eq "--create") {
|
||||
$create = 1;
|
||||
}
|
||||
if ($arg eq "--apply") {
|
||||
$apply = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $apply && ! $create ) {
|
||||
print "Argument missing: Please use either --apply or --create.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $apply && $create ) {
|
||||
print "Argument error: Cannot use both --apply and --create.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($create) {
|
||||
my $misp_config_path = "$misp_config_path/misp.conf.d";
|
||||
make_path($misp_config_path);
|
||||
|
||||
unless(-e "$misp_config_path/Internal.conf") {
|
||||
open(my $internalfh, ">", "$misp_config_path/Internal.conf");
|
||||
print $internalfh "redis_host localhost\n";
|
||||
close $internalfh;
|
||||
}
|
||||
|
||||
die "foo";
|
||||
my $settings = `sudo -u $misp_user $misp_path/app/Console/cake admin getSetting all`;
|
||||
my @lines = split("\n",$settings);
|
||||
my $value = 0;
|
||||
my $setting = 0;
|
||||
foreach(@lines) {
|
||||
if ($_ =~ m/\"value\": (.*)/) {
|
||||
$value = substr $1, 0, -1;
|
||||
}
|
||||
if ($_ =~ m/\"setting\": \"(.*)\"/) {
|
||||
$setting = $1;
|
||||
}
|
||||
if ($value && $setting) {
|
||||
#print "$setting -> $value\n";
|
||||
my $category = "";
|
||||
my $key = "";
|
||||
my $section = "";
|
||||
|
||||
# Plugin.S3_aws_secret_key -> false
|
||||
# ^ ^
|
||||
# | |- Section (S3)
|
||||
# | |- Key (S3_aws_secret_key)
|
||||
# |-- Category
|
||||
#
|
||||
# We also have:
|
||||
# Proxy.host -> ""
|
||||
# Which won't create a section because of the missing _
|
||||
if ($setting =~ m/(\w+)\.(\w+)/) {
|
||||
$category = $1;
|
||||
$key = $2;
|
||||
# print "Setting category: $1 and key: $2\n";
|
||||
if ($key =~ m/(.*?)_/) {
|
||||
$section = $1
|
||||
# print "Section: $1\n";
|
||||
} else { # There is no _, so the section is the key
|
||||
$section = $key;
|
||||
}
|
||||
} else {
|
||||
# Orphan settings are debug settings:
|
||||
$category = "Debug";
|
||||
$key = $setting;
|
||||
# print "Orphan setting:$setting\n";
|
||||
}
|
||||
|
||||
#
|
||||
# We have all we needed, we can write the files
|
||||
#
|
||||
my $conf_to_write = "$misp_config_path/$category.conf";
|
||||
unless($CATEGORIES_CREATED{"$category"}) {
|
||||
if (-e $conf_to_write) {
|
||||
die "We cannot overwrite $conf_to_write, it already exists. Remove it manually.\n";
|
||||
}
|
||||
}
|
||||
$CATEGORIES_CREATED{"$category"} = 1;
|
||||
open(my $fh, ">>", "$conf_to_write");
|
||||
print $fh "$key $value\n";
|
||||
close $fh;
|
||||
|
||||
# Unset those two variables to be filled again!
|
||||
$value = 0;
|
||||
$setting = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($apply) {
|
||||
my $misp_config_path = "$misp_config_path/misp.conf.d";
|
||||
|
||||
unless(-d $misp_config_path) {
|
||||
die "MISP Configuration Path does not exists: $misp_config_path\n";
|
||||
}
|
||||
|
||||
foreach my $fp (glob("$misp_config_path/*.conf")) {
|
||||
my $category = substr($fp, length($misp_config_path)+1, -5);
|
||||
my $cake_config = $category eq "Internal" ? 0 : 1;
|
||||
# print "$category:$cake_config\n";
|
||||
# print "$fp\n";
|
||||
open(my $fh, "<", $fp) or die "$!";
|
||||
while (my $line = <$fh>) {
|
||||
chomp $line;
|
||||
$line =~ /(\S+) (.*)/;
|
||||
my $key = "";
|
||||
|
||||
if ($category eq "Debug") {
|
||||
# The Debug category does not... have a category
|
||||
$key = $1;
|
||||
} elsif ($category eq "Internal") {
|
||||
# Internal means we are patching portions of the code to configure
|
||||
if ($1 eq "redis_host") {
|
||||
replace_inplace_content("$misp_path/app/Lib/Tools/PubSubTool.php", "localhost", $2);
|
||||
replace_inplace_content("$misp_path/app/Plugin/CakeResque/Config/config.php", "localhost", $2);
|
||||
}
|
||||
} else {
|
||||
$key = "$category.$1";
|
||||
}
|
||||
|
||||
if ($cake_config) {
|
||||
system("sudo -u $misp_user $misp_path/app/Console/cake admin setSetting $key $2");
|
||||
}
|
||||
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue