diff --git a/.drone.yml b/.drone.yml
index bec436627a..1d73a49aa7 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1,16 +1,9 @@
---
kind: pipeline
name: default
-
-# Disable default clone
-clone:
- disable: true
+type: docker
steps:
- # This clone step doesn't use "root" user
- - name: clone
- image: plugins/git:next
-
# Restore cache of downloaded dependencies
- name: restore-cache
image: drillster/drone-volume-cache
@@ -27,8 +20,7 @@ steps:
- name: run-tests
image: thehiveproject/drone-scala-node
commands:
- - . ~/.nvm/nvm.sh
- - sbt -Duser.home=$PWD test stage
+ - sbt -Duser.home=$PWD test:compile test
# Build packages
- name: build-packages
@@ -37,9 +29,23 @@ steps:
pgp_key: {from_secret: pgp_key}
commands:
- |
+ V=$(sbt -no-colors --error "print thehive/version" | tail -1)
+ if ( echo $V | grep -qi snapshot)
+ then
+ exit 1
+ fi
. ~/.nvm/nvm.sh
[ -n "$PLUGIN_PGP_KEY" ] && gpg --batch --import - <<< $PLUGIN_PGP_KEY
sbt -Duser.home=$PWD docker:stage debian:packageBin rpm:packageBin universal:packageBin
+ if ( echo $V | grep -qi rc )
+ then
+ echo $( echo $V | sed -re 's/([0-9]+.[0-9]+.[0-9]+)-RC([0-9]+)-([0-9]+)/\1-RC\2,\1-RC\2-\3/' ) > .tags
+ else
+ echo $( echo $V | sed -re 's/([0-9]+).([0-9]+).([0-9]+)-([0-9]+)/\1,\1.\2,\1.\2.\3,\1.\2.\3-\4,latest/' ) > .tags
+ fi
+ echo $V > thehive-version.txt
+ mv target/rpm/RPMS/noarch/thehive*.rpm target/
+ mv target/universal/thehive*.zip target/
when:
event: [tag]
@@ -48,156 +54,89 @@ steps:
image: drillster/drone-volume-cache
settings:
rebuild: true
+ backend: "filesystem"
mount:
- .sbt
- .ivy2
+ - .cache
- ui/node_modules
- ui/bower_components
volumes: [{name: cache, path: /cache}]
- - name: publish-package
- image: thehiveproject/drone-bintray
+ # Send packages using scp
+ - name: send packages
+ image: appleboy/drone-scp
settings:
- user: {from_secret: bintray_user}
- key: {from_secret: bintray_key}
- subject: thehive-project
- package: thehive
- commands:
- - |
- export PLUGIN_USER
- export PLUGIN_KEY
- export PLUGIN_SUBJECT
- export PLUGIN_PACKAGE
- export PLUGIN_VERSION=$(cut -d\" -f2 version.sbt)
- echo "Publishing package version $PLUGIN_VERSION"
-
- if echo $PLUGIN_VERSION | grep -qvi -E \
- -e '^[0-9]+\.[0-9]+\.[0-9]+$' \
- -e '^[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$' \
- -e '^[0-9]+\.[0-9]+\.[0-9]+-RC[0-9]+$'; then
- echo The version $PLUGIN_VERSION has invalid format
- exit 1
- fi
-
- CHANNEL=stable
- if $(echo $PLUGIN_VERSION | grep -qi rc)
- then
- CHANNEL=beta
- V=$(echo $PLUGIN_VERSION | sed -e 's/-\([rR][cC]\)/-0.1\1/')
- DEB_FILE=target/thehive_$${V}_all.deb
- RPM_FILE=target/rpm/RPMS/noarch/thehive-$${V}.noarch.rpm
- else
- DEB_FILE=target/thehive_$${PLUGIN_VERSION}_all.deb
- RPM_FILE=target/rpm/RPMS/noarch/thehive-$${PLUGIN_VERSION}.noarch.rpm
- fi
-
- ZIP_FILE=target/universal/thehive-$${PLUGIN_VERSION}.zip
-
- upload \
- --file $DEB_FILE \
- --repo debian-beta \
- --extra-param deb_distribution=any \
- --extra-param deb_component=main \
- --extra-param deb_architecture=all
-
- [ $CHANNEL = stable ] && upload \
- --file $DEB_FILE \
- --repo debian-stable \
- --extra-param deb_distribution=any \
- --extra-param deb_component=main \
- --extra-param deb_architecture=all
-
- upload \
- --file $RPM_FILE \
- --repo rpm-beta
-
- [ $CHANNEL = stable ] && upload \
- --file $RPM_FILE \
- --repo rpm-stable
-
- upload \
- --file $ZIP_FILE \
- --repo binary
-
- LATEST_VERSION=latest
- [ $CHANNEL = beta ] && LATEST_VERSION=latest-beta
-
- removeVersion \
- --repo binary \
- --version $LATEST_VERSION
+ host: {from_secret: package_host}
+ username: {from_secret: package_user}
+ key: {from_secret: package_key}
+ target: {from_secret: incoming_path}
+ source:
+ - target/thehive*.deb
+ - target/thehive*.rpm
+ - target/thehive*.zip
+ strip_components: 1
+ when:
+ event: [tag]
- upload \
- --file $ZIP_FILE \
- --repo binary \
- --version $LATEST_VERSION \
- --dest-file thehive-$${LATEST_VERSION}.zip
+ # Publish packages
+ - name: publish packages
+ image: appleboy/drone-ssh
+ settings:
+ host: {from_secret: package_host}
+ user: {from_secret: package_user}
+ key: {from_secret: package_key}
+ publish_script: {from_secret: publish_script}
+ commands:
+ - PLUGIN_SCRIPT="bash $PLUGIN_PUBLISH_SCRIPT thehive $(cat thehive-version.txt)" /bin/drone-ssh
when:
event: [tag]
- # Publish docker image
+ # Publish docker image on Docker Hub
- name: docker
image: plugins/docker
settings:
context: target/docker/stage
dockerfile: target/docker/stage/Dockerfile
repo: thehiveproject/thehive
- auto_tag: true
username: {from_secret: docker_username}
password: {from_secret: docker_password}
when:
event: [tag]
- # Deploy binaries in integration environment
- - name: copy binaries in integration environment
- image: appleboy/drone-scp
- settings:
- host: {from_secret: deploy_beta_host}
- username: {from_secret: deploy_username}
- key: {from_secret: deploy_key}
- target: ./thehive-builds/${DRONE_BUILD_NUMBER}
- source: target/universal/stage
- strip_components: 3
- when:
- branch: [develop]
- event: {exclude: [pull_request]}
-
- - name: deploy binaries in integration environment
- image: appleboy/drone-ssh
- settings:
- host: {from_secret: deploy_beta_host}
- username: {from_secret: deploy_username}
- key: {from_secret: deploy_key}
- script:
- - ./start thehive ${DRONE_BUILD_NUMBER}
- when:
- branch: [develop]
- event: {exclude: [pull_request]}
-
- # Deploy binaries in staging environment
- - name: copy binaries in staging environment
- image: appleboy/drone-scp
+ # Publish docker image on Harbor
+ - name: harbor
+ image: plugins/docker
settings:
- host: {from_secret: deploy_stable_host}
- username: {from_secret: deploy_username}
- key: {from_secret: deploy_key}
- target: ./thehive-builds/${DRONE_BUILD_NUMBER}
- source: target/universal/stage
- strip_components: 3
+ context: target/docker/stage
+ dockerfile: target/docker/stage/Dockerfile
+ registry: {from_secret: harbor_registry}
+ repo: {from_secret: harbor_repo}
+ username: {from_secret: harbor_username}
+ password: {from_secret: harbor_password}
when:
- branch: [master]
- event: {exclude: [pull_request]}
+ event: [tag]
- - name: deploy binaries in staging environment
- image: appleboy/drone-ssh
+ - name: send message
+ image: thehiveproject/drone_keybase
settings:
- host: {from_secret: deploy_stable_host}
- username: {from_secret: deploy_username}
- key: {from_secret: deploy_key}
- script:
- - ./start thehive ${DRONE_BUILD_NUMBER}
+ username: {from_secret: keybase_username}
+ paperkey: {from_secret: keybase_paperkey}
+ channel: {from_secret: keybase_channel}
+ commands:
+ - |
+ keybase oneshot -u "$PLUGIN_USERNAME" --paperkey "$PLUGIN_PAPERKEY"
+ URL="$DRONE_SYSTEM_PROTO://$DRONE_SYSTEM_HOST/$DRONE_REPO/$DRONE_BUILD_NUMBER"
+ if [ $DRONE_BUILD_STATUS = "success" ]
+ then
+ keybase chat send "$PLUGIN_CHANNEL" ":white_check_mark: $DRONE_REPO: build succeeded $URL"
+ else
+ keybase chat send "$PLUGIN_CHANNEL" ":x: $DRONE_REPO: build failed $URL"
+ fi
when:
- branch: [master]
- event: {exclude: [pull_request]}
+ status:
+ - success
+ - failure
volumes:
- name: cache
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31f7833080..8825d1ec63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Change Log
+## [3.5.0-RC1](https://github.com/TheHive-Project/TheHive/milestone/44) (2020-08-12)
+
+**Implemented enhancements:**
+
+- Support of ElasticSearch 7 [\#1377](https://github.com/TheHive-Project/TheHive/issues/1377)
+- [Enhancement] MISP sync [\#1398](https://github.com/TheHive-Project/TheHive/issues/1398)
+
+**Closed issues:**
+
+- OAuth2 not working : Authentication failure [\#946](https://github.com/TheHive-Project/TheHive/issues/946)
+
+**Fixed bugs:**
+
+- [Bug] OAuth2/OpenIDC Authentication failure [\#1291](https://github.com/TheHive-Project/TheHive/issues/1291)
+- [Feature Request] OAuth support for Basic authentication to authorization server's tokenUrl [\#1294](https://github.com/TheHive-Project/TheHive/issues/1294)
+- [Bug] Can't auth with SSO/OAuth with FusionAuth [\#1342](https://github.com/TheHive-Project/TheHive/issues/1342)
+- [Bug] TheHive is stalled while importing Alerts with a large number of observables [\#1416](https://github.com/TheHive-Project/TheHive/issues/1416)
+
## [3.4.2](https://github.com/TheHive-Project/TheHive/milestone/57) (2020-04-25)
**Implemented enhancements:**
diff --git a/build.sbt b/build.sbt
index 82efa88501..992f41566f 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,5 +1,6 @@
import Common._
import Dependencies._
+import org.thp.ghcl.Milestone
lazy val thehiveBackend = (project in file("thehive-backend"))
.enablePlugins(PlayScala)
@@ -17,6 +18,7 @@ lazy val thehiveBackend = (project in file("thehive-backend"))
Library.zip4j,
Library.reflections,
Library.akkaCluster,
+ Library.akkaClusterTyped,
Library.akkaClusterTools
),
play.sbt.routes.RoutesKeys.routesImport -= "controllers.Assets.Asset"
@@ -105,6 +107,8 @@ rpmReleaseFile := {
rpmFile
}
+milestoneFilter := ((milestone: Milestone) ⇒ milestone.title.head < '4')
+
bintrayOrganization := Some("thehive-project")
// Front-end //
diff --git a/contrib/.gitignore b/contrib/.gitignore
deleted file mode 100644
index 71d918b0bb..0000000000
--- a/contrib/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-report-templates/*.zip
diff --git a/contrib/report-templates/Abuse_Finder_1.0/long.html b/contrib/report-templates/Abuse_Finder_1.0/long.html
deleted file mode 100644
index ee902b1b50..0000000000
--- a/contrib/report-templates/Abuse_Finder_1.0/long.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
- Abuse Finder Information for {{artifact.data}}
-
-
-
- - Names:
- -
-
- {{name}}
-
-
-
-
- - Abuse addresses:
- -
-
- {{abuse}}
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/DNSDB_DomainName_1_1/long.html b/contrib/report-templates/DNSDB_DomainName_1_1/long.html
deleted file mode 100644
index cb1eb66e8e..0000000000
--- a/contrib/report-templates/DNSDB_DomainName_1_1/long.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
- DNSDB Domain Name History Report (RRSET)
-
-
-
-
- # |
- bailiwick |
- count |
- rdata |
- rrname |
- time first |
- time last |
-
-
-
-
- {{$index+1}} |
- {{row.bailiwick}} |
- {{row.count}} |
-
- {{rdata}}
- |
- {{row.rrname}} |
- {{(row.zone_time_first || row.time_first) | shortDate}} |
- {{(row.zone_time_last || row.time_last) | shortDate}} |
-
-
-
-
-
diff --git a/contrib/report-templates/DNSDB_DomainName_1_1/short.html b/contrib/report-templates/DNSDB_DomainName_1_1/short.html
deleted file mode 100644
index f0ae84c374..0000000000
--- a/contrib/report-templates/DNSDB_DomainName_1_1/short.html
+++ /dev/null
@@ -1 +0,0 @@
-DNSDB Domain Name: {{content.records}} records
diff --git a/contrib/report-templates/DNSDB_IPHistory_1_0/long.html b/contrib/report-templates/DNSDB_IPHistory_1_0/long.html
deleted file mode 100644
index 5cb5ced89b..0000000000
--- a/contrib/report-templates/DNSDB_IPHistory_1_0/long.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
- DNSDB IP History Report
- (Rdata IP)
-
-
-
-
- # |
- count |
- rdata |
- rrname |
- rrtype |
- time first |
- time last |
-
-
-
- {{$index + 1}} |
- {{row.count}} |
- {{row.rdata}} |
- class="wrap"{{row.rrname}} |
- {{row.rrtype}} |
- {{(row.zone_time_first || row.time_first) | shortDate}} |
- {{(row.zone_time_last || row.time_last) | shortDate}} |
-
-
-
-
diff --git a/contrib/report-templates/DNSDB_IPHistory_1_0/short.html b/contrib/report-templates/DNSDB_IPHistory_1_0/short.html
deleted file mode 100644
index 4402a33419..0000000000
--- a/contrib/report-templates/DNSDB_IPHistory_1_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-DNSDB IP History: {{content.records}} records
diff --git a/contrib/report-templates/DNSDB_NameHistory_1_0/long.html b/contrib/report-templates/DNSDB_NameHistory_1_0/long.html
deleted file mode 100644
index d7031c9a69..0000000000
--- a/contrib/report-templates/DNSDB_NameHistory_1_0/long.html
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
- DNSDB Name History Report(Rdata Name)
-
-
-
-
- # |
- count |
- rdata |
- rrname |
- rrtype |
- time first |
- time last |
-
-
-
- {{$index + 1}} |
- {{row.count}} |
- {{row.rdata}} |
- {{row.rrname}} |
- {{row.rrtype}} |
- {{row.time_first | shortDate}} |
- {{row.time_last | shortDate}} |
-
-
-
-
diff --git a/contrib/report-templates/DNSDB_NameHistory_1_0/short.html b/contrib/report-templates/DNSDB_NameHistory_1_0/short.html
deleted file mode 100644
index 3271f99b00..0000000000
--- a/contrib/report-templates/DNSDB_NameHistory_1_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-DNSDB Name History: {{content.records}} records
diff --git a/contrib/report-templates/DomainTools_ReverseIP_1_0/long.html b/contrib/report-templates/DomainTools_ReverseIP_1_0/long.html
deleted file mode 100644
index 89c1bc2164..0000000000
--- a/contrib/report-templates/DomainTools_ReverseIP_1_0/long.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
-
- - Domains count
- - {{content.ip_addresses.domain_count}}
-
-
- - Domain Names
- -
-
-
-
-
-
-
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/DomainTools_ReverseIP_1_0/short.html b/contrib/report-templates/DomainTools_ReverseIP_1_0/short.html
deleted file mode 100644
index 8b91e7d98e..0000000000
--- a/contrib/report-templates/DomainTools_ReverseIP_1_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-{{content.ip.address}}: {{content.ip.domain_count}} domains found
diff --git a/contrib/report-templates/DomainTools_ReverseNameServer_1_0/long.html b/contrib/report-templates/DomainTools_ReverseNameServer_1_0/long.html
deleted file mode 100644
index 470227e411..0000000000
--- a/contrib/report-templates/DomainTools_ReverseNameServer_1_0/long.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
- {{artifact.data | fang}}
-
-
-
- - Name Server
- - {{content.name_server.hostname}}
-
-
- - Primary domains
- -
- {{content.name_server.primary}}
- Show all
-
-
-
- - Secondary domains
- -
- {{content.name_server.secondary}}
- Show all
-
-
-
-
-
Primary Domains List
-
{{content.primary_domains | json}}
-
-
-
Secondary Domains List
-
{{content.secondary_domains | json}}
-
-
-
-
diff --git a/contrib/report-templates/DomainTools_ReverseNameServer_1_0/short.html b/contrib/report-templates/DomainTools_ReverseNameServer_1_0/short.html
deleted file mode 100644
index 2f4c374ea0..0000000000
--- a/contrib/report-templates/DomainTools_ReverseNameServer_1_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-Hostname: {{content.name_server}}, {{content.domain_count}} domains
diff --git a/contrib/report-templates/DomainTools_ReverseWhois_1_0/long.html b/contrib/report-templates/DomainTools_ReverseWhois_1_0/long.html
deleted file mode 100644
index b2f8fe07bb..0000000000
--- a/contrib/report-templates/DomainTools_ReverseWhois_1_0/long.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
-
- - Current Domains
- - {{content.domain_count.current}}
-
-
- - Historic Domains
- - {{content.domain_count.historic}}
-
-
- - Domains
- -
-
-
-
-
-
-
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/DomainTools_ReverseWhois_1_0/short.html b/contrib/report-templates/DomainTools_ReverseWhois_1_0/short.html
deleted file mode 100644
index 015b9f0fa0..0000000000
--- a/contrib/report-templates/DomainTools_ReverseWhois_1_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Domains found: curr:{{content.domain_count.current}}/hist:{{content.domain_count.historic}}
-
diff --git a/contrib/report-templates/DomainTools_WhoisHistory_1_0/long.html b/contrib/report-templates/DomainTools_WhoisHistory_1_0/long.html
deleted file mode 100644
index 3d235f6000..0000000000
--- a/contrib/report-templates/DomainTools_WhoisHistory_1_0/long.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
No records found
-
-
-
- # |
- date |
- server names |
- registrant |
- registrar |
- expiration |
- statuses |
-
-
-
- {{$index + 1}} |
- {{row.date}} |
-
-
- |
- {{row.whois.registrant}} |
- {{row.whois.registration.registrar}} |
- {{row.whois.registration.expires}} |
-
-
- |
-
-
-
-
diff --git a/contrib/report-templates/DomainTools_WhoisHistory_1_0/short.html b/contrib/report-templates/DomainTools_WhoisHistory_1_0/short.html
deleted file mode 100644
index 3e748cbb2c..0000000000
--- a/contrib/report-templates/DomainTools_WhoisHistory_1_0/short.html
+++ /dev/null
@@ -1,2 +0,0 @@
-REGISTRANT: {{content.registrant}}
-REGISTRAR: {{content.registrar}}
diff --git a/contrib/report-templates/DomainTools_WhoisLookup_1_0/long.html b/contrib/report-templates/DomainTools_WhoisLookup_1_0/long.html
deleted file mode 100644
index 2cdab86513..0000000000
--- a/contrib/report-templates/DomainTools_WhoisLookup_1_0/long.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
- {{artifact.data | fang}}
-
-
-
- - Date of record
- - {{content.whois.date}}
-
-
{{content.whois.record}}
-
-
diff --git a/contrib/report-templates/DomainTools_WhoisLookup_1_0/short.html b/contrib/report-templates/DomainTools_WhoisLookup_1_0/short.html
deleted file mode 100644
index 3e748cbb2c..0000000000
--- a/contrib/report-templates/DomainTools_WhoisLookup_1_0/short.html
+++ /dev/null
@@ -1,2 +0,0 @@
-REGISTRANT: {{content.registrant}}
-REGISTRAR: {{content.registrar}}
diff --git a/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/long.html b/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/long.html
deleted file mode 100644
index 2cdab86513..0000000000
--- a/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/long.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
- {{artifact.data | fang}}
-
-
-
- - Date of record
- - {{content.whois.date}}
-
-
{{content.whois.record}}
-
-
diff --git a/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/short.html b/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/short.html
deleted file mode 100644
index f95ed23abc..0000000000
--- a/contrib/report-templates/DomainTools_WhoisLookup_IP_1_0/short.html
+++ /dev/null
@@ -1,2 +0,0 @@
-DT:Whois:REGISTRANT= {{content.registrant}}
-DT:Whois:REGISTRAR= {{content.registrar}}
diff --git a/contrib/report-templates/File_Info_1_0/long.html b/contrib/report-templates/File_Info_1_0/long.html
deleted file mode 100644
index 7d72d879d9..0000000000
--- a/contrib/report-templates/File_Info_1_0/long.html
+++ /dev/null
@@ -1,391 +0,0 @@
-
-
-
-
-
-
- File Identification
-
-
-
- - MD5
- - {{content.Identification['MD5']}}
-
-
- - SHA1
- - {{content.Identification['SHA1']}}
-
-
- - SHA256
- - {{content.Identification['SHA256']}}
-
-
- - impash
- - {{content.Identification['impash']|| "-"}}
-
-
-
- - ssdeep
- - {{content.Identification['ssdeep']|| "-"}}
-
-
-
- - pehash
- - {{content.Identification['pehash']|| "-"}}
-
-
-
- - Operating System
- - {{content.Identification['OperatingSystem']}}
-
-
-
- - PE Type
- - {{content.Identification['PEType']}}
-
-
- - Magic literal
- - {{content.Magic}}
-
-
- - MimeType
- - {{content.Mimetype}}
-
-
-
-
-
-
-
- File Metadata (Exiftool)
-
-
-
-
-
-
-
- PE Basic Information
-
-
-
- - {{I.Info}}
- - {{I.Value}}
-
-
- - Compilation Timestamp
- - {{content.PE.BasicInformation.CompilationTimestamp}}
-
-
- - File Size
- - {{content.PE.BasicInformation.FileSize}}
-
-
- - Entry Point (EP)
- - {{content.PE.BasicInformation.EntryPoint}}
-
-
- - Target Machine
- - {{content.PE.BasicInformation.TargetMachine}}
-
-
-
-
-
-
-
- PE Sections
-
-
-
-
- Section |
- SizeOfRawData |
- Entroy |
-
-
-
-
- {{section.entryname}}
- |
- {{section.SizeOfRawData}} |
- {{section.Entropy}} |
-
-
-
-
- -
- MD5
-
- -
- {{section.MD5}}
-
-
-
- -
- SHA1
-
- -
- {{section.SHA1}}
-
-
-
- -
- SHA256
-
- -
- {{section.SHA256}}
-
-
- |
-
-
-
-
-
-
-
-
-
-
- PE Import Address Table
-
-
-
- -
-
-
-
- {{entry.entryname}}
-
-
- -
-
- {{entry.symbols.length}}
- items
-
-
-
-
-
-
-
-
-
-
- Olevba Report
-
-
-
Summary
-
- - Olevba version:
- - v{{content.MSOffice.olevba.Version}}
-
-
-
- - Olevba detection :
- - {{content.MSOffice.olevba.vba}}
-
-
-
- - Olevba scanner :
- -
-
-
-
- Not suspicious
- Suspicious VBA
-
-
- Base64 strings
-
-
- Hex strings
-
-
-
-
- Not suspicious
-
-
-
-
-
-
-
-
Detailed Information
-
-
-
-
-
OLE stream:
- {{stream['OLE stream']}}
-
-
-
Information
-
- - VBA filename:
- - {{stream['VBA filename']}}
-
-
- - Filename:
- - {{stream['Filename']}}
-
-
-
-
Olevba analysis
-
-
-
-
- Type |
- Keyword |
- Description |
-
-
-
-
- {{result.type}} |
- {{result.keyword}} |
- {{result.description}} |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Analysis failure
-
-
- {{content.MSOffice.olevba.Error}}
-
-
-
-
-
-
-
-
-
-
-
-
- PDFiD Report
-
-
-
Summary
-
- - PDFiD version:
- - v{{content.PDF.pdfid[0].pdfid.version}}
-
-
-
- - Suspicious:
- - {{content.PDF.pdfid[0].suspicious}}
-
-
-
- - PDFiD detection :
- -
-
-
-
- /RichMedia
-
-
-
-
- /OpenAction
-
-
-
-
- /JavaScript
-
-
-
-
- /Launch
-
-
-
-
- /ObjStm
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/File_Info_1_0/short.html b/contrib/report-templates/File_Info_1_0/short.html
deleted file mode 100644
index f8d67bb184..0000000000
--- a/contrib/report-templates/File_Info_1_0/short.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- {{content.filetype}}
-
-
-
- {{content.filetype}}: Suspicious
-
diff --git a/contrib/report-templates/Fortiguard_URLCategory_1_0/long.html b/contrib/report-templates/Fortiguard_URLCategory_1_0/long.html
deleted file mode 100644
index 251350036f..0000000000
--- a/contrib/report-templates/Fortiguard_URLCategory_1_0/long.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/Fortiguard_URLCategory_1_0/short.html b/contrib/report-templates/Fortiguard_URLCategory_1_0/short.html
deleted file mode 100644
index e39dbcf7f7..0000000000
--- a/contrib/report-templates/Fortiguard_URLCategory_1_0/short.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
- URLCat:
- {{content.category}}
-
diff --git a/contrib/report-templates/HippoMore_1_0/long.html b/contrib/report-templates/HippoMore_1_0/long.html
deleted file mode 100644
index d0f1417e4d..0000000000
--- a/contrib/report-templates/HippoMore_1_0/long.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
- Detailed Information
-
-
-
- No records found
-
-
-
- Source |
- First seen by source |
- Last seen by source |
- Category |
- Details |
-
-
- {{source.source}} |
- {{source.first_seen || '-'}} |
- {{source.last_seen || '-'}} |
- {{source.category || '-'}} |
-
- First added in DB: {{source.firstAppearance }}
- Last added in DB: {{source.lastAppearance }} |
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/HippoMore_1_0/short.html b/contrib/report-templates/HippoMore_1_0/short.html
deleted file mode 100644
index 5812622426..0000000000
--- a/contrib/report-templates/HippoMore_1_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-HippoMore: {{content[artifact.data]}} record(s)
diff --git a/contrib/report-templates/Hipposcore_1_0/long.html b/contrib/report-templates/Hipposcore_1_0/long.html
deleted file mode 100644
index 3fd59af53e..0000000000
--- a/contrib/report-templates/Hipposcore_1_0/long.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- Detailed Information
-
-
-
- - Hippocamp Score:
- -
-
- {{score}}
-
-
- {{score}}
-
-
- {{score}}
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/Hipposcore_1_0/short.html b/contrib/report-templates/Hipposcore_1_0/short.html
deleted file mode 100644
index 34ef106075..0000000000
--- a/contrib/report-templates/Hipposcore_1_0/short.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
- HippoScore: {{score}}
-
diff --git a/contrib/report-templates/MaxMind_GeoIP_2_0/long.html b/contrib/report-templates/MaxMind_GeoIP_2_0/long.html
deleted file mode 100644
index 5f98f12d29..0000000000
--- a/contrib/report-templates/MaxMind_GeoIP_2_0/long.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- Geolocation of {{(artifact.data || artifact.attachment.name) | fang}}
-
-
-
Is anonymous proxy
-
Is satellite provider
-
- {{[content.continent.name, content.country.name, content.subdivisions.name, content.city.name].join(' / ')}}
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/MaxMind_GeoIP_2_0/short.html b/contrib/report-templates/MaxMind_GeoIP_2_0/short.html
deleted file mode 100644
index 0c79843709..0000000000
--- a/contrib/report-templates/MaxMind_GeoIP_2_0/short.html
+++ /dev/null
@@ -1 +0,0 @@
-IP location: {{content.country}} / {{content.continent}}
diff --git a/contrib/report-templates/Msg_Parser_1_0/long.html b/contrib/report-templates/Msg_Parser_1_0/long.html
deleted file mode 100644
index 1291294fcd..0000000000
--- a/contrib/report-templates/Msg_Parser_1_0/long.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
- Email message details
-
-
-
Is anonymous proxy
-
Is satellite provider
-
-
- - From
- - {{content.displayFrom}} ({{content.sender}})
-
-
- - To
- - {{content.displayTo}} ({{content.receivers}})
-
-
- - Subject
- - {{content.subject || '-'}}
-
-
- - Topic
- - {{content.topic || '-'}}
-
-
- - Bcc
- - {{content.bcc || '-'}}
-
-
- - Attachments
- -
-
This message file includes
-
-
-
-
-
- Filename |
- Mime Type |
- Extension |
-
-
-
-
- {{a.filename}} |
- {{a.mime}} |
- {{a.extension}} |
-
-
-
-
-
-
- - Headers
- -
-
{{content.headers}}
-
-
-
- - Body
- -
-
{{content.body}}
-
-
-
-
diff --git a/contrib/report-templates/Msg_Parser_1_0/short.html b/contrib/report-templates/Msg_Parser_1_0/short.html
deleted file mode 100644
index 60802dd732..0000000000
--- a/contrib/report-templates/Msg_Parser_1_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- MsgParser:
-
diff --git a/contrib/report-templates/OTXQuery_1_0/long.html b/contrib/report-templates/OTXQuery_1_0/long.html
deleted file mode 100644
index f1c39b6739..0000000000
--- a/contrib/report-templates/OTXQuery_1_0/long.html
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
- OTX Report
-
-
-
-
- - ERROR:
- - {{content.errortext}}
-
-
-
- - Related Pulses Found:
- - {{content.pulse_count}}
-
-
-
- - Related Pulses:
- -
-
-
Name: {{::pulse.name}}
-
Author: {{::pulse.author.username}}
-
Modified: {{::pulse.modified_text}} @ {{::pulse.modified}}
-
Subscribers: {{::pulse.subscriber_count}}
-
Subscribed: {{::pulse.is_subscribing}}
-
Industries: {{::pulse.industries}}
-
Indicators: {{::pulse.indicator_count}}
-
-
-
-
-
-
- - Submit Pulse:
- -
- Create a Pulse
-
-
-
-
- - Malware Samples:
- -
-
- {{::sample.sample}}
-
-
-
-
-
- - Malware:
- - {{content.malware}}
-
-
-
- - SHA1:
- - {{content.sha1}}
-
-
-
- - SHA256:
- - {{content.sha256}}
-
-
-
- - MD5:
- - {{content.md5}}
-
-
-
- - Page Type:
- - {{content.page_type}}
-
-
-
- - File Class:
- - {{content.file_class}}
-
-
-
- - File Type:
- - {{content.file_type}}
-
-
-
- - File Size:
- - {{content.filesize}}
-
-
-
- - SSDEEP:
- - {{content.ssdeep}}
-
-
-
- - Related URLs:
- -
-
- URL: {{::url.url}}
- Date: {{::url.date}}
- HTTP Code: {{::url.httpcode}}
- IP: {{::url.result.urlworker.ip}}
-
-
-
-
-
-
- - Passive DNS:
- -
-
- Hostname: {{::dns.hostname}}
- IP: {{::dns.address}}
- First/Last seen: {{::dns.first}} / {{::dns.last}}
- Locale: {{::dns.flag_title}}
-
-
-
-
-
-
- - Whois:
- -
- Whois Query
-
-
-
-
- - Alexa:
- -
- Alexa Report
-
-
-
-
- - City:
- - {{content.city}}
-
-
-
- - Country Code:
- - {{content.country_code}}
-
-
-
- - Country:
- - {{content.country_name}}
-
-
-
- - ASN:
- - {{content.asn}}
-
-
-
-
diff --git a/contrib/report-templates/OTXQuery_1_0/short.html b/contrib/report-templates/OTXQuery_1_0/short.html
deleted file mode 100644
index 43a71a067f..0000000000
--- a/contrib/report-templates/OTXQuery_1_0/short.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
- OTX:
- Pulses({{content.pulse_count}})
-
diff --git a/contrib/report-templates/PassiveTotal_Enrichment_1_0/long.html b/contrib/report-templates/PassiveTotal_Enrichment_1_0/long.html
deleted file mode 100644
index 694882d8de..0000000000
--- a/contrib/report-templates/PassiveTotal_Enrichment_1_0/long.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
- PassiveTotal Enrichment Info
-
-
-
- No records found
-
-
-
- - {{key}}
- -
-
-
- {{tag}}
-
-
- {{subd}}
-
-
- {{value || None}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Malware_1_0/long.html b/contrib/report-templates/PassiveTotal_Malware_1_0/long.html
deleted file mode 100644
index f95a957ac1..0000000000
--- a/contrib/report-templates/PassiveTotal_Malware_1_0/long.html
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
- PassiveTotal OSINT
-
-
-
- No records found
-
-
-
-
-
- - {{key}}:
- -
-
-
- {{value || None}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Malware_1_0/short.html b/contrib/report-templates/PassiveTotal_Malware_1_0/short.html
deleted file mode 100644
index 733f0f9b6e..0000000000
--- a/contrib/report-templates/PassiveTotal_Malware_1_0/short.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- PT:MALWARE=False
-
-
-
- PT:MALWARE=True
-
diff --git a/contrib/report-templates/PassiveTotal_Osint_1_0/long.html b/contrib/report-templates/PassiveTotal_Osint_1_0/long.html
deleted file mode 100644
index 8715acf54c..0000000000
--- a/contrib/report-templates/PassiveTotal_Osint_1_0/long.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
- PassiveTotal OSINT
-
-
-
- No records found
-
-
-
-
-
- - {{key}}:
- -
-
-
- {{v}}
-
-
- {{v}}
-
-
- {{value || None}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Osint_1_0/short.html b/contrib/report-templates/PassiveTotal_Osint_1_0/short.html
deleted file mode 100644
index 0c7db25dbd..0000000000
--- a/contrib/report-templates/PassiveTotal_Osint_1_0/short.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- PT:OSINT=False
-
-
-
- PT:OSINT=True
-
diff --git a/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/long.html b/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/long.html
deleted file mode 100644
index d9b74792cd..0000000000
--- a/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/long.html
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
- PassiveTotal PassiveDNS Report
-
-
-
- No records found
-
-
-
- Summary Information
-
-
-
- - Value:
- - {{content.queryValue}}
-
-
- - Total Records:
- - {{content.totalRecords}}
-
-
- - First seen:
- - {{content.firstSeen}}
-
-
- - Last seen:
- - {{content.lastSeen}}
-
-
-
-
-
- Records
-
-
-
-
- Source |
- Resolve |
- First seen |
- Last seen |
-
-
-
-
- {{s}}
-
- |
- {{c.resolve || 'None'}} |
- {{c.firstSeen || 'None'}} |
- {{c.lastSeen || 'None'}} |
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/short.html b/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/short.html
deleted file mode 100644
index 9de1bbc44c..0000000000
--- a/contrib/report-templates/PassiveTotal_Passive_Dns_1_0/short.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- PT:PassiveDNS= {{content.total}} record
-
-
-
- PT:PassiveDNS= {{content.total}} record(s)
-
diff --git a/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/long.html b/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/long.html
deleted file mode 100644
index 33a4d38445..0000000000
--- a/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/long.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
- PassiveTotal SSL Certificate Information
-
-
-
- No records found
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/short.html b/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/short.html
deleted file mode 100644
index 6f79d3155b..0000000000
--- a/contrib/report-templates/PassiveTotal_Ssl_Certificate_Details_1_0/short.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- PT:SSL=False
-
-
-
- PT:SSL=True
-
diff --git a/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/long.html b/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/long.html
deleted file mode 100644
index 0ef8fe306b..0000000000
--- a/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/long.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
- Detailed Information
-
-
-
- No records found
-
-
-
- SHA1 |
- First seen |
- Last seen |
-
-
- {{c.sha1}} |
- {{c.firstSeen || 'None'}} |
- {{c.lastSeen || 'None'}} |
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/short.html b/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/short.html
deleted file mode 100644
index 128ecbc3b0..0000000000
--- a/contrib/report-templates/PassiveTotal_Ssl_Certificate_History_1_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- PT:SSLCertHistory= {{content.total}} record(s)
-
diff --git a/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/long.html b/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/long.html
deleted file mode 100644
index d7b0b64f5e..0000000000
--- a/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/long.html
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
- PassiveTotal unique resolution
-
-
-
- No records found
-
-
-
- - Query Type:
- - {{content.queryType}}
-
-
- - Query:
- - {{content.queryValue}}
-
-
- -
-
-
-
- {{content.queryValue}} :
- -
-
- {{content.total}} result(s)
-
-
-
- {{content.results[0]}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/short.html b/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/short.html
deleted file mode 100644
index 54e803b3ae..0000000000
--- a/contrib/report-templates/PassiveTotal_Unique_Resolutions_1_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- PT:UniqueResolution= {{content.total}} record(s)
-
diff --git a/contrib/report-templates/PassiveTotal_Whois_Details_1.0/long.html b/contrib/report-templates/PassiveTotal_Whois_Details_1.0/long.html
deleted file mode 100644
index 4d3b96114d..0000000000
--- a/contrib/report-templates/PassiveTotal_Whois_Details_1.0/long.html
+++ /dev/null
@@ -1,118 +0,0 @@
-
-
-
-
-
- PassiveTotal Whois Summary
-
-
-
- No records found
-
-
-
- - Domain:
- - {{content.domain|| "-"}}
-
-
- - Contact email:
- - {{content.contactEmail || "-"}}
-
-
- - Nameservers:
- - {{ns}}
-
-
- - Whois server:
- - {{content.whoisServer || "-"}}
-
-
- - Registered date:
- - {{content.registered}}
-
-
- - Registry updated at:
- - {{content.registryUpdatedAt}}
-
-
- - Last load at:
- - {{content.lastLoadedAt}}
-
-
- - Expires at:
- - {{content.expiresAt}}
-
-
- - Registrar:
- - {{content.registrar}}
-
-
-
-
-
-
-
- PassiveTotal Whois Admin Info
-
-
-
- No records found
-
-
-
- - {{key}}
- - {{value}}
-
-
-
-
-
-
-
- PassiveTotal Whois Tech Info
-
-
-
- No records found
-
-
-
- - {{key}}
- - {{value}}
-
-
-
-
-
-
-
- PassiveTotal Whois Registrant Info
-
-
-
- No records found
-
-
-
- - {{key}}
- - {{value}}
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PassiveTotal_Whois_Details_1.0/short.html b/contrib/report-templates/PassiveTotal_Whois_Details_1.0/short.html
deleted file mode 100644
index 7353be9a41..0000000000
--- a/contrib/report-templates/PassiveTotal_Whois_Details_1.0/short.html
+++ /dev/null
@@ -1,2 +0,0 @@
-PT:Whois:REGISTRANT= {{content.registrant}}
-PT:Whois:REGISTRAR= {{content.registrar}}
diff --git a/contrib/report-templates/PhishTank_CheckURL_1_0/long.html b/contrib/report-templates/PhishTank_CheckURL_1_0/long.html
deleted file mode 100644
index c10bf30991..0000000000
--- a/contrib/report-templates/PhishTank_CheckURL_1_0/long.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PhishTank_CheckURL_1_0/short.html b/contrib/report-templates/PhishTank_CheckURL_1_0/short.html
deleted file mode 100644
index b90f191fcb..0000000000
--- a/contrib/report-templates/PhishTank_CheckURL_1_0/short.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- PhishTank:
-
- {{millis | amDurationFormat : 'milliseconds'}}
-
-
-
- PhishTank: {{content.in_database}}
-
-
-
-
diff --git a/contrib/report-templates/PhishingInitiative_Lookup_1_0/long.html b/contrib/report-templates/PhishingInitiative_Lookup_1_0/long.html
deleted file mode 100644
index 1f573461e5..0000000000
--- a/contrib/report-templates/PhishingInitiative_Lookup_1_0/long.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- PhishingInitiative Report for {{artifact.data | fang}}
-
-
-
- - Status:
- -
- {{content.tag_label}}
-
-
-
-
-
-
- {{artifact.data | fang}}
-
-
- {{content.errorMessage}}
-
-
diff --git a/contrib/report-templates/PhishingInitiative_Lookup_1_0/short.html b/contrib/report-templates/PhishingInitiative_Lookup_1_0/short.html
deleted file mode 100644
index d200285563..0000000000
--- a/contrib/report-templates/PhishingInitiative_Lookup_1_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- PhishingInitiative: {{content.status}}
-
diff --git a/contrib/report-templates/VirusTotal_GetReport_2_0/long.html b/contrib/report-templates/VirusTotal_GetReport_2_0/long.html
deleted file mode 100644
index fdcb8194fe..0000000000
--- a/contrib/report-templates/VirusTotal_GetReport_2_0/long.html
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
- Summary
-
-
-
- - Score
- - {{content.positives || 0}}/{{content.total}}
-
-
- - Last analysis date
- - {{content.scan_date}}
-
-
- - Autonomous System
- - {{content.as_owner}}
-
-
- - Categories
- - {{content.categories.join(', ')}}
-
-
- - Sub domains
- - {{content.subdomains.join(', ')}}
-
-
- - Resolutions
- -
-
- This domain has been seen to resolve to the following IP addresses.
-
-
- The following domains resolved to the given IP address.
-
-
- {{::resolution.last_resolved | amDateFormat:'DD-MM-YYYY'}}:
- {{(resolution.ip_address | fang) || (resolution.hostname | fang)}}
-
-
-
-
- - Virus Total
- -
-
-
-
- View Full Report
-
-
-
-
- View Full Report
-
-
-
-
- View Full Report
-
-
-
-
-
-
-
-
-
-
Latest URLs hosted in this IP address
- detected by at least one URL scanner or malicious URL dataset.
-
-
-
- Score |
- Scan Date |
- URL |
-
-
-
- {{::url.positives}}/{{::url.total}}
- |
- {{url.scan_date}} |
- {{url.url | fang}} |
-
-
-
-
-
-
-
-
-
Latest files that are
- detected by at least one antivirus solution and were downloaded by VirusTotal from the IP address provided.
-
-
-
- Score |
- Date |
- SHA256 |
-
-
-
- {{hash.positives}}/{{hash.total}}
- |
- {{hash.date}} |
- {{hash.sha256}} |
-
-
-
-
-
-
-
-
-
Latest files that are
- detected by at least one antivirus solution and embed URL pattern strings with the IP address provided.
-
-
-
- Score |
- SHA256 |
-
-
-
- {{hash.positives}}/{{hash.total}}
- |
- {{hash.sha256}} |
-
-
-
-
-
-
-
- Scans
-
-
-
-
-
- Scanner |
- Detected |
- Result |
- Details |
- Update |
- Version |
-
-
-
- {{scanner}}
- |
-
-
- |
- {{result.result}} |
-
-
-
- View details
- |
- {{result.update}} |
- {{result.version}} |
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.verbose_msg}}
-
-
-
-
diff --git a/contrib/report-templates/VirusTotal_GetReport_2_0/short.html b/contrib/report-templates/VirusTotal_GetReport_2_0/short.html
deleted file mode 100644
index a713b763d4..0000000000
--- a/contrib/report-templates/VirusTotal_GetReport_2_0/short.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
- VT:
- {{content.positives}}/{{content.total}}
- Scans({{content.scans}})
- Resolutions({{content.resolutions}})
- Url detections({{content.detected_urls}})
- files({{content.detected_downloaded_samples}})
-
diff --git a/contrib/report-templates/VirusTotal_Scan_2_0/long.html b/contrib/report-templates/VirusTotal_Scan_2_0/long.html
deleted file mode 100644
index 8bdaee6579..0000000000
--- a/contrib/report-templates/VirusTotal_Scan_2_0/long.html
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
- {{(artifact.data || artifact.attachment.name) | fang}}
-
-
- {{content.errorMessage}}
-
-
-
-
-
-
-
-
Summary
-
-
- - Score
- - {{content.positives}}/{{content.total}}
-
-
- - Last analysis date
- - {{content.scan_date}}
-
-
- - Virus Total
- -
-
-
-
- View Full Report
-
-
-
-
-
-
-
-
Scans
-
-
-
-
- Scanner |
- Detected |
- Result |
- Details |
- Update |
- Version |
-
-
- {{scanner}} |
-
-
- |
- {{result.result}} |
-
- View details
- |
- {{result.update}} |
- {{result.version}} |
-
-
-
-
-
-
-
-
-
-
- {{(artifact.data || artifact.attachment.name)| fang}}
-
-
- {{content.verbose_msg}}
-
-
-
-
diff --git a/contrib/report-templates/VirusTotal_Scan_2_0/short.html b/contrib/report-templates/VirusTotal_Scan_2_0/short.html
deleted file mode 100644
index 7da455c08f..0000000000
--- a/contrib/report-templates/VirusTotal_Scan_2_0/short.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- VT: {{content.positives}}/{{content.total}}
-
diff --git a/debian.sbt b/debian.sbt
index 2f08116b02..7a40c44ea7 100644
--- a/debian.sbt
+++ b/debian.sbt
@@ -1,12 +1,13 @@
-import Common.{stableVersion, snapshotVersion, betaVersion}
+import Common.{betaVersion, snapshotVersion, stableVersion, versionUsage}
-linuxPackageMappings in Debian += packageMapping(file("LICENSE") -> "/usr/share/doc/thehive/copyright").withPerms("644")
+linuxPackageMappings in Debian += packageMapping(file("LICENSE") → "/usr/share/doc/thehive/copyright").withPerms("644")
version in Debian := {
version.value match {
- case stableVersion(_, _) => version.value
- case betaVersion(v1, v2) => v1 + "-0.1RC" + v2
- case snapshotVersion(_, _) => version.value + "-SNAPSHOT"
- case _ => sys.error("Invalid version: " + version.value)
+ case stableVersion(_, _) ⇒ version.value
+ case betaVersion(v1, v2, v3) ⇒ v1 + "-0." + v3 + "RC" + v2
+ case snapshotVersion(stableVersion(v1, v2)) ⇒ v1 + "-" + v2 + "-SNAPSHOT"
+ case snapshotVersion(betaVersion(v1, v2, v3)) ⇒ v1 + "-0." + v3 + "RC" + v2 + "-SNAPSHOT"
+ case _ ⇒ versionUsage(version.value)
}
}
debianPackageRecommends := Seq("elasticsearch")
@@ -16,4 +17,4 @@ maintainerScripts in Debian := maintainerScriptsFromDirectory(
Seq(DebianConstants.Postinst, DebianConstants.Prerm, DebianConstants.Postrm)
)
linuxEtcDefaultTemplate in Debian := (baseDirectory.value / "package" / "etc_default_thehive").asURL
-linuxMakeStartScript in Debian := None
\ No newline at end of file
+linuxMakeStartScript in Debian := None
diff --git a/docker.sbt b/docker.sbt
index e96488b4cf..4619e82c53 100644
--- a/docker.sbt
+++ b/docker.sbt
@@ -1,12 +1,13 @@
-import Common.{betaVersion, snapshotVersion, stableVersion}
+import Common.{betaVersion, snapshotVersion, stableVersion, versionUsage}
import com.typesafe.sbt.packager.docker.{Cmd, ExecCmd}
version in Docker := {
version.value match {
- case stableVersion(_, _) ⇒ version.value
- case betaVersion(v1, v2) ⇒ v1 + "-0.1RC" + v2
- case snapshotVersion(_, _) ⇒ version.value + "-SNAPSHOT"
- case _ ⇒ sys.error("Invalid version: " + version.value)
+ case stableVersion(_, _) ⇒ version.value
+ case betaVersion(v1, v2, v3) ⇒ v1 + "-0." + v3 + "RC" + v2
+ case snapshotVersion(stableVersion(v1, v2)) ⇒ v1 + "-" + v2 + "-SNAPSHOT"
+ case snapshotVersion(betaVersion(v1, v2, v3)) ⇒ v1 + "-0." + v3 + "RC" + v2 + "-SNAPSHOT"
+ case _ ⇒ versionUsage(version.value)
}
}
defaultLinuxInstallLocation in Docker := "/opt/thehive"
diff --git a/project/Common.scala b/project/Common.scala
index a382ef5ee9..afa8373da3 100644
--- a/project/Common.scala
+++ b/project/Common.scala
@@ -15,15 +15,15 @@ object Common {
scalaVersion := Dependencies.scalaVersion,
scalacOptions ++= Seq(
"-deprecation", // Emit warning and location for usages of deprecated APIs.
- "-feature", // Emit warning and location for usages of features that should be imported explicitly.
- "-unchecked", // Enable additional warnings where generated code depends on assumptions.
+ "-feature", // Emit warning and location for usages of features that should be imported explicitly.
+ "-unchecked", // Enable additional warnings where generated code depends on assumptions.
//"-Xfatal-warnings", // Fail the compilation if there are any warnings.
- "-Xlint", // Enable recommended additional warnings.
- "-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver.
- "-Ywarn-dead-code", // Warn when dead code is identified.
- "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures.
+ "-Xlint", // Enable recommended additional warnings.
+ "-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver.
+ "-Ywarn-dead-code", // Warn when dead code is identified.
+ "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures.
"-Ywarn-nullary-override", // Warn when non-nullary overrides nullary, e.g. def foo() over def foo.
- "-Ywarn-numeric-widen" // Warn when numerics are widened.
+ "-Ywarn-numeric-widen" // Warn when numerics are widened.
),
scalacOptions in Test ~= { options ⇒
options filterNot (_ == "-Ywarn-dead-code") // Allow dead code in tests (to support using mockito).
@@ -31,21 +31,28 @@ object Common {
parallelExecution in Test := false,
fork in Test := true,
javaOptions += "-Xmx1G",
-
// Redirect logs from ElasticSearch (which uses log4j2) to slf4j
libraryDependencies += "org.apache.logging.log4j" % "log4j-to-slf4j" % "2.9.1",
excludeDependencies += "org.apache.logging.log4j" % "log4j-core"
)
val stableVersion: Regex = "(\\d+\\.\\d+\\.\\d+)-(\\d+)".r
- val betaVersion: Regex = "(\\d+\\.\\d+\\.\\d+)-[Rr][Cc](\\d+)".r
+ val betaVersion: Regex = "(\\d+\\.\\d+\\.\\d+)-[Rr][Cc](\\d+)-(\\d+)".r
+
object snapshotVersion {
- def unapplySeq(version: String): Option[List[String]] = {
- if (version.endsWith("-SNAPSHOT")) {
- val v = version.dropRight(9)
- stableVersion.unapplySeq(v) orElse betaVersion.unapplySeq(v)
- }
+
+ def unapply(version: String): Option[String] =
+ if (version.endsWith("-SNAPSHOT")) Some(version.dropRight(9))
else None
- }
}
-}
\ No newline at end of file
+
+ def versionUsage(version: String): Nothing =
+ sys.error(
+ s"Invalid version: $version\n" +
+ "The accepted formats for version are:\n" +
+ " - 1.2.3-4\n" +
+ " - 1.2.3-RC4-5\n" +
+ " - 1.2.3-4-SNAPSHOT\n" +
+ " - 1.2.3-RC4-5-SNAPSHOT"
+ )
+}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 7be462cf0c..6e341910f8 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -1,27 +1,27 @@
import sbt._
object Dependencies {
- val scalaVersion = "2.12.6"
+ val scalaVersion = "2.12.11"
object Library {
object Play {
- val version = play.core.PlayVersion.current
- val ws = "com.typesafe.play" %% "play-ws" % version
- val ahc = "com.typesafe.play" %% "play-ahc-ws" % version
- val cache = "com.typesafe.play" %% "play-ehcache" % version
- val test = "com.typesafe.play" %% "play-test" % version
- val specs2 = "com.typesafe.play" %% "play-specs2" % version
- val filters = "com.typesafe.play" %% "filters-helpers" % version
- val guice = "com.typesafe.play" %% "play-guice" % version
+ val ws = "com.typesafe.play" %% "play-ws" % play.core.PlayVersion.current
+ val ahc = "com.typesafe.play" %% "play-ahc-ws" % play.core.PlayVersion.current
+ val cache = "com.typesafe.play" %% "play-ehcache" % play.core.PlayVersion.current
+ val test = "com.typesafe.play" %% "play-test" % play.core.PlayVersion.current
+ val specs2 = "com.typesafe.play" %% "play-specs2" % play.core.PlayVersion.current
+ val filters = "com.typesafe.play" %% "filters-helpers" % play.core.PlayVersion.current
+ val guice = "com.typesafe.play" %% "play-guice" % play.core.PlayVersion.current
}
- val scalaGuice = "net.codingwell" %% "scala-guice" % "4.2.3"
+ val scalaGuice = "net.codingwell" %% "scala-guice" % "4.2.6"
- val reflections = "org.reflections" % "reflections" % "0.9.11"
- val zip4j = "net.lingala.zip4j" % "zip4j" % "1.3.2"
- val elastic4play = "org.thehive-project" %% "elastic4play" % "1.11.5"
- val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % "2.5.21"
- val akkaClusterTools = "com.typesafe.akka" %% "akka-cluster-tools" % "2.5.21"
+ val reflections = "org.reflections" % "reflections" % "0.9.11"
+ val zip4j = "net.lingala.zip4j" % "zip4j" % "2.6.0"
+ val elastic4play = "org.thehive-project" %% "elastic4play" % "1.12.1"
+ val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % play.core.PlayVersion.akkaVersion
+ val akkaClusterTyped = "com.typesafe.akka" %% "akka-cluster-typed" % play.core.PlayVersion.akkaVersion
+ val akkaClusterTools = "com.typesafe.akka" %% "akka-cluster-tools" % play.core.PlayVersion.akkaVersion
}
}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 8069c5c167..efb8586d60 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,7 +1,7 @@
// Comment to get more information during initialization
logLevel := Level.Info
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.23")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.2")
addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1")
-addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.0.0")
+addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0")
addSbtPlugin("org.thehive-project" % "sbt-github-changelog" % "0.3.0")
diff --git a/rpm.sbt b/rpm.sbt
index d65451081f..fa852a9bd6 100644
--- a/rpm.sbt
+++ b/rpm.sbt
@@ -1,21 +1,24 @@
-import Common.{stableVersion, snapshotVersion, betaVersion}
+import Common.{betaVersion, snapshotVersion, stableVersion, versionUsage}
version in Rpm := {
version.value match {
- case stableVersion(v1, v2) => v1
- case betaVersion(v1, v2) => v1
- case snapshotVersion(v1, v2) => v1
- case _ => sys.error("Invalid version: " + version.value)
+ case stableVersion(v1, _) ⇒ v1
+ case betaVersion(v1, _, _) ⇒ v1
+ case snapshotVersion(stableVersion(v1, _)) ⇒ v1
+ case snapshotVersion(betaVersion(v1, _, _)) ⇒ v1
+ case _ ⇒ versionUsage(version.value)
}
}
rpmRelease := {
version.value match {
- case stableVersion(_, v2) => v2
- case betaVersion(v1, v2) => "0.1RC" + v2
- case snapshotVersion(v1, v2) => v2 + "-SNAPSHOT"
- case _ => sys.error("Invalid version: " + version.value)
+ case stableVersion(_, v2) ⇒ v2
+ case betaVersion(_, v2, v3) ⇒ "0." + v3 + "RC" + v2
+ case snapshotVersion(stableVersion(_, v2)) ⇒ v2 + "-SNAPSHOT"
+ case snapshotVersion(betaVersion(_, v2, v3)) ⇒ "0." + v3 + "RC" + v2 + "-SNAPSHOT"
+ case _ ⇒ versionUsage(version.value)
}
}
+
rpmVendor := organizationName.value
rpmUrl := organizationHomepage.value.map(_.toString)
rpmLicense := Some("AGPL")
@@ -35,12 +38,14 @@ linuxPackageMappings in Rpm := configWithNoReplace((linuxPackageMappings in Rpm)
packageBin in Rpm := {
import scala.sys.process._
val rpmFile = (packageBin in Rpm).value
- Process("rpm" ::
- "--define" :: "_gpg_name TheHive Project" ::
- "--define" :: "_signature gpg" ::
- "--define" :: "__gpg_check_password_cmd /bin/true" ::
- "--define" :: "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor --use-agent --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" ::
- "--addsign" :: rpmFile.toString ::
- Nil).!!
+ Process(
+ "rpm" ::
+ "--define" :: "_gpg_name TheHive Project" ::
+ "--define" :: "_signature gpg" ::
+ "--define" :: "__gpg_check_password_cmd /bin/true" ::
+ "--define" :: "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor --use-agent --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" ::
+ "--addsign" :: rpmFile.toString ::
+ Nil
+ ).!!
rpmFile
}
diff --git a/thehive-backend/app/connectors/Connectors.scala b/thehive-backend/app/connectors/Connectors.scala
index fe3d0982d6..3668976de1 100644
--- a/thehive-backend/app/connectors/Connectors.scala
+++ b/thehive-backend/app/connectors/Connectors.scala
@@ -1,13 +1,12 @@
package connectors
import scala.collection.immutable
-
import play.api.libs.json.{JsObject, Json}
import play.api.mvc._
import play.api.routing.sird.UrlContext
import play.api.routing.{Router, SimpleRouter}
-
import com.google.inject.AbstractModule
+import scala.reflect.runtime.{universe => ru}
import javax.inject.{Inject, Singleton}
import models.HealthStatus
import net.codingwell.scalaguice.{ScalaModule, ScalaMultibinder}
@@ -35,7 +34,7 @@ class ConnectorRouter @Inject()(connectors: immutable.Set[Connector], actionBuil
abstract class ConnectorModule extends AbstractModule with ScalaModule {
- def registerController[C <: Connector](implicit evidence: Manifest[C]): Unit = {
+ def registerController[C <: Connector: ru.TypeTag]: Unit = {
val connectorBindings = ScalaMultibinder.newSetBinder[Connector](binder)
connectorBindings.addBinding.to[C]
()
diff --git a/thehive-backend/app/controllers/ArtifactCtrl.scala b/thehive-backend/app/controllers/ArtifactCtrl.scala
index 3bde2e0543..646c49c1e0 100644
--- a/thehive-backend/app/controllers/ArtifactCtrl.scala
+++ b/thehive-backend/app/controllers/ArtifactCtrl.scala
@@ -3,25 +3,24 @@ package controllers
import java.io.FilterInputStream
import java.nio.file.{Files, Path}
-import scala.collection.JavaConverters._
-import scala.concurrent.{ExecutionContext, Future}
-
+import javax.inject.{Inject, Singleton}
+import models.Roles
+import net.lingala.zip4j.ZipFile
import play.api.http.Status
import play.api.libs.json.{JsArray, JsValue}
import play.api.mvc._
import play.api.{Configuration, Logger}
-import javax.inject.{Inject, Singleton}
-import models.Roles
-import net.lingala.zip4j.core.ZipFile
+import scala.collection.JavaConverters._
+import scala.concurrent.{ExecutionContext, Future}
+//import net.lingala.zip4j.core.ZipFile
import net.lingala.zip4j.model.FileHeader
-import services.ArtifactSrv
-
import org.elastic4play.controllers._
import org.elastic4play.models.JsonFormat.baseModelEntityWrites
import org.elastic4play.services.JsonFormat.{aggReads, queryReads}
import org.elastic4play.services._
import org.elastic4play.{BadRequestError, InternalError, InvalidFormatAttributeError, Timed}
+import services.ArtifactSrv
@Singleton
class ArtifactCtrl @Inject()(
@@ -70,7 +69,7 @@ class ArtifactCtrl @Inject()(
}
}
- private def getFieldsFromZipFile(caseId: String, fields: Fields, filepath: Path)(implicit authContext: AuthContext): Seq[Fields] = {
+ private def getFieldsFromZipFile(fields: Fields, filepath: Path)(implicit authContext: AuthContext): Seq[Fields] = {
val zipFile = new ZipFile(filepath.toFile)
val files: Seq[FileHeader] = zipFile.getFileHeaders.asScala.asInstanceOf[Seq[FileHeader]]
@@ -79,7 +78,7 @@ class ArtifactCtrl @Inject()(
.getString("zipPassword")
.filterNot(_.isEmpty)
.getOrElse(configuration.get[String]("datastore.attachment.password"))
- zipFile.setPassword(pw)
+ zipFile.setPassword(pw.toCharArray)
}
/*val multiFields = */
@@ -125,7 +124,7 @@ class ArtifactCtrl @Inject()(
.get("attachment")
.map {
case FileInputValue(_, filepath, _) if fields.getBoolean("isZip").getOrElse(false) ⇒
- Future.successful(getFieldsFromZipFile(caseId, fields, filepath))
+ Future.successful(getFieldsFromZipFile(fields, filepath))
case _: FileInputValue ⇒ Future.successful(Seq(fields))
case JsonInputValue(JsArray(attachments)) ⇒
Future.traverse(attachments)(attachment ⇒ getFieldsFromAttachment(fields, attachment)).map(_.flatten)
@@ -155,7 +154,7 @@ class ArtifactCtrl @Inject()(
}
@Timed
- def get(id: String): Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { implicit request ⇒
+ def get(id: String): Action[Fields] = authenticated(Roles.read).async(fieldsBodyParser) { _ ⇒
artifactSrv
.get(id)
.map(artifact ⇒ renderer.toOutput(OK, artifact))
diff --git a/thehive-backend/app/controllers/AttachmentCtrl.scala b/thehive-backend/app/controllers/AttachmentCtrl.scala
index 46dce09a56..b66aebe746 100644
--- a/thehive-backend/app/controllers/AttachmentCtrl.scala
+++ b/thehive-backend/app/controllers/AttachmentCtrl.scala
@@ -1,23 +1,21 @@
package controllers
import java.nio.file.Files
-import javax.inject.{Inject, Singleton}
-
-import play.api.http.HttpEntity
-import play.api.libs.Files.DefaultTemporaryFileCreator
-import play.api.mvc._
-import play.api.{mvc, Configuration}
import akka.stream.scaladsl.FileIO
-import net.lingala.zip4j.core.ZipFile
-import net.lingala.zip4j.model.ZipParameters
-import net.lingala.zip4j.util.Zip4jConstants
+import javax.inject.{Inject, Singleton}
import models.Roles
-
+import net.lingala.zip4j.ZipFile
+import net.lingala.zip4j.model.ZipParameters
+import net.lingala.zip4j.model.enums.{CompressionLevel, EncryptionMethod}
import org.elastic4play.Timed
-import org.elastic4play.controllers.{Authenticated, Renderer}
+import org.elastic4play.controllers.Authenticated
import org.elastic4play.models.AttachmentAttributeFormat
import org.elastic4play.services.AttachmentSrv
+import play.api.http.HttpEntity
+import play.api.libs.Files.DefaultTemporaryFileCreator
+import play.api.mvc._
+import play.api.{Configuration, mvc}
/**
* Controller used to access stored attachments (plain or zipped)
@@ -29,7 +27,6 @@ class AttachmentCtrl(
attachmentSrv: AttachmentSrv,
authenticated: Authenticated,
components: ControllerComponents,
- renderer: Renderer
) extends AbstractController(components) {
@Inject() def this(
@@ -38,9 +35,8 @@ class AttachmentCtrl(
attachmentSrv: AttachmentSrv,
authenticated: Authenticated,
components: ControllerComponents,
- renderer: Renderer
) =
- this(configuration.get[String]("datastore.attachment.password"), tempFileCreator, attachmentSrv, authenticated, components, renderer)
+ this(configuration.get[String]("datastore.attachment.password"), tempFileCreator, attachmentSrv, authenticated, components)
/**
* Download an attachment, identified by its hash, in plain format
@@ -48,7 +44,7 @@ class AttachmentCtrl(
* open the document directly. It must be used only for safe file
*/
@Timed("controllers.AttachmentCtrl.download")
- def download(hash: String, name: Option[String]): Action[AnyContent] = authenticated(Roles.read) { implicit request ⇒
+ def download(hash: String, name: Option[String]): Action[AnyContent] = authenticated(Roles.read) { _ ⇒
if (hash.startsWith("{{")) // angularjs hack
NoContent
else if (!name.getOrElse("").intersect(AttachmentAttributeFormat.forbiddenChar).isEmpty)
@@ -69,20 +65,21 @@ class AttachmentCtrl(
* File name can be specified (zip extension is append)
*/
@Timed("controllers.AttachmentCtrl.downloadZip")
- def downloadZip(hash: String, name: Option[String]): Action[AnyContent] = authenticated(Roles.read) { implicit request ⇒
+ def downloadZip(hash: String, name: Option[String]): Action[AnyContent] = authenticated(Roles.read) { _ ⇒
if (!name.getOrElse("").intersect(AttachmentAttributeFormat.forbiddenChar).isEmpty)
BadRequest("File name is invalid")
else {
val f = tempFileCreator.create("zip", hash).path
Files.delete(f)
val zipFile = new ZipFile(f.toFile)
+ zipFile.setPassword(password.toCharArray)
val zipParams = new ZipParameters
- zipParams.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_FASTEST)
+ zipParams.setCompressionLevel(CompressionLevel.FASTEST)
zipParams.setEncryptFiles(true)
- zipParams.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD)
- zipParams.setPassword(password)
+ zipParams.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD)
+// zipParams.setsetPassword(password.toCharArray)
zipParams.setFileNameInZip(name.getOrElse(hash))
- zipParams.setSourceExternalStream(true)
+// zipParams.setSourceExternalStream(true)
zipFile.addStream(attachmentSrv.stream(hash), zipParams)
Result(
diff --git a/thehive-backend/app/controllers/AuditCtrl.scala b/thehive-backend/app/controllers/AuditCtrl.scala
index 128a96e277..49002b4803 100644
--- a/thehive-backend/app/controllers/AuditCtrl.scala
+++ b/thehive-backend/app/controllers/AuditCtrl.scala
@@ -30,7 +30,7 @@ class AuditCtrl @Inject()(
* Return audit logs. For each item, include ancestor entities
*/
@Timed
- def flow(rootId: Option[String], count: Option[Int]): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def flow(rootId: Option[String], count: Option[Int]): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
val (audits, total) = auditSrv(rootId.filterNot(_ == "any"), count.getOrElse(10))
renderer.toOutput(OK, audits, total)
}
diff --git a/thehive-backend/app/controllers/AuthenticationCtrl.scala b/thehive-backend/app/controllers/AuthenticationCtrl.scala
index cae6774be5..42a127388b 100644
--- a/thehive-backend/app/controllers/AuthenticationCtrl.scala
+++ b/thehive-backend/app/controllers/AuthenticationCtrl.scala
@@ -1,12 +1,12 @@
package controllers
import javax.inject.{Inject, Singleton}
-
import models.UserStatus
-import org.elastic4play.controllers.{Authenticated, Fields, FieldsBodyParser, Renderer}
+import org.elastic4play.controllers.{Authenticated, Fields, FieldsBodyParser}
import org.elastic4play.database.DBIndex
import org.elastic4play.services.AuthSrv
-import org.elastic4play.{AuthorizationError, OAuth2Redirect, Timed}
+import org.elastic4play.{AuthorizationError, Timed}
+import play.api.Configuration
import play.api.mvc._
import services.UserSrv
@@ -14,11 +14,11 @@ import scala.concurrent.{ExecutionContext, Future}
@Singleton
class AuthenticationCtrl @Inject()(
+ configuration: Configuration,
authSrv: AuthSrv,
userSrv: UserSrv,
authenticated: Authenticated,
dbIndex: DBIndex,
- renderer: Renderer,
components: ControllerComponents,
fieldsBodyParser: FieldsBodyParser,
implicit val ec: ExecutionContext
@@ -46,24 +46,23 @@ class AuthenticationCtrl @Inject()(
dbIndex.getIndexStatus.flatMap {
case false ⇒ Future.successful(Results.Status(520))
case _ ⇒
- (for {
- authContext ← authSrv.authenticate()
- user ← userSrv.get(authContext.userId)
- } yield {
- if (user.status() == UserStatus.Ok)
- authenticated.setSessingUser(Ok, authContext)
- else
- throw AuthorizationError("Your account is locked")
- }) recover {
- // A bit of a hack with the status code, so that Angular doesn't reject the origin
- case OAuth2Redirect(redirectUrl, qp) ⇒ Redirect(redirectUrl, qp, status = OK)
- case e ⇒ throw e
- }
+ authSrv
+ .authenticate()
+ .flatMap {
+ case Right(authContext) ⇒
+ userSrv.get(authContext.userId).map { user ⇒
+ if (user.status() == UserStatus.Ok)
+ authenticated.setSessingUser(Redirect(configuration.get[String]("play.http.context").stripSuffix("/") + "/index.html"), authContext)
+ else
+ throw AuthorizationError("Your account is locked")
+ }
+ case Left(result) ⇒ Future.successful(result)
+ }
}
}
@Timed
- def logout = Action {
+ def logout: Action[AnyContent] = Action {
Ok.withNewSession
}
}
diff --git a/thehive-backend/app/controllers/CaseCtrl.scala b/thehive-backend/app/controllers/CaseCtrl.scala
index a3654677a0..1160f20686 100644
--- a/thehive-backend/app/controllers/CaseCtrl.scala
+++ b/thehive-backend/app/controllers/CaseCtrl.scala
@@ -126,7 +126,7 @@ class CaseCtrl @Inject()(
}
@Timed
- def linkedCases(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def linkedCases(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
caseSrv
.linkedCases(id)
.runWith(Sink.seq)
diff --git a/thehive-backend/app/controllers/CaseTemplateCtrl.scala b/thehive-backend/app/controllers/CaseTemplateCtrl.scala
index 66292283c3..091b4b45b3 100644
--- a/thehive-backend/app/controllers/CaseTemplateCtrl.scala
+++ b/thehive-backend/app/controllers/CaseTemplateCtrl.scala
@@ -36,7 +36,7 @@ class CaseTemplateCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
caseTemplateSrv
.get(id)
.map(caze ⇒ renderer.toOutput(OK, caze))
diff --git a/thehive-backend/app/controllers/CustomFieldsCtrl.scala b/thehive-backend/app/controllers/CustomFieldsCtrl.scala
index a5a7b38b35..4975e243a8 100644
--- a/thehive-backend/app/controllers/CustomFieldsCtrl.scala
+++ b/thehive-backend/app/controllers/CustomFieldsCtrl.scala
@@ -1,17 +1,15 @@
package controllers
import scala.concurrent.{ExecutionContext, Future}
-
import play.api.http.Status
import play.api.libs.json.{JsNumber, JsObject, Json}
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
-
import akka.stream.Materializer
import akka.stream.scaladsl.Sink
-import com.sksamuel.elastic4s.http.ElasticDsl.{search, termsAggregation}
+import com.sksamuel.elastic4s.ElasticDsl.{search, termsAgg}
+import com.sksamuel.elastic4s.requests.searches.aggs.responses.bucket.Terms
import javax.inject.{Inject, Singleton}
import models.Roles
-
import org.elastic4play.NotFoundError
import org.elastic4play.controllers.Authenticated
import org.elastic4play.database.DBFind
@@ -43,9 +41,9 @@ class CustomFieldsCtrl @Inject()(
.flatMap { customFieldType ⇒
val filter = and("relations" in ("case", "alert", "caseTemplate"), contains(s"customFields.$customField.$customFieldType"))
dbfind(
- indexName ⇒ search(indexName).query(filter.query).aggregations(termsAggregation("t").field("relations"))
+ indexName ⇒ search(indexName).query(filter.query).aggregations(termsAgg("t","relations"))
).map { searchResponse ⇒
- val buckets = searchResponse.aggregations.terms("t").buckets
+ val buckets = searchResponse.aggregations.result[Terms]("t").buckets
val total = buckets.map(_.docCount).sum
val result = buckets.map(b ⇒ b.key → JsNumber(b.docCount)) :+ ("total" → JsNumber(total))
Ok(JsObject(result))
diff --git a/thehive-backend/app/controllers/DBListCtrl.scala b/thehive-backend/app/controllers/DBListCtrl.scala
index d4c6a68df3..74496673d6 100644
--- a/thehive-backend/app/controllers/DBListCtrl.scala
+++ b/thehive-backend/app/controllers/DBListCtrl.scala
@@ -23,14 +23,14 @@ class DBListCtrl @Inject()(
) extends AbstractController(components) {
@Timed("controllers.DBListCtrl.list")
- def list: Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def list: Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
dblists.listAll.map { listNames ⇒
renderer.toOutput(OK, listNames)
}
}
@Timed("controllers.DBListCtrl.listItems")
- def listItems(listName: String): Action[AnyContent] = authenticated(Roles.read) { implicit request ⇒
+ def listItems(listName: String): Action[AnyContent] = authenticated(Roles.read) { _ ⇒
val (src, _) = dblists(listName).getItems[JsValue]
val items = src
.map { case (id, value) ⇒ s""""$id":$value""" }
diff --git a/thehive-backend/app/controllers/DashboardCtrl.scala b/thehive-backend/app/controllers/DashboardCtrl.scala
index 901a0b7dc5..b4311da67a 100644
--- a/thehive-backend/app/controllers/DashboardCtrl.scala
+++ b/thehive-backend/app/controllers/DashboardCtrl.scala
@@ -21,7 +21,6 @@ import org.elastic4play.{AuthorizationError, BadRequestError, Timed}
@Singleton
class DashboardCtrl @Inject()(
dashboardSrv: DashboardSrv,
- auxSrv: AuxSrv,
authenticated: Authenticated,
renderer: Renderer,
components: ControllerComponents,
@@ -41,7 +40,7 @@ class DashboardCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
dashboardSrv.get(id).map { dashboard ⇒
renderer.toOutput(OK, dashboard)
}
diff --git a/thehive-backend/app/controllers/DescribeCtrl.scala b/thehive-backend/app/controllers/DescribeCtrl.scala
index 24829e7b57..5e00dbc370 100644
--- a/thehive-backend/app/controllers/DescribeCtrl.scala
+++ b/thehive-backend/app/controllers/DescribeCtrl.scala
@@ -33,7 +33,7 @@ class DescribeCtrl @Inject()(
Json.obj("label" → model.label, "path" → model.path, "attributes" → attributeDefinitions)
}
- def describe(modelName: String): Action[AnyContent] = authenticated(Roles.read) { implicit request ⇒
+ def describe(modelName: String): Action[AnyContent] = authenticated(Roles.read) { _ ⇒
modelSrv(modelName)
.map { model ⇒
renderer.toOutput(OK, modelToJson(model))
@@ -43,7 +43,7 @@ class DescribeCtrl @Inject()(
private val allModels: Seq[String] = Seq("case", "case_artifact", "case_task", "case_task_log", "alert", "case_artifact_job", "audit", "action")
- def describeAll: Action[AnyContent] = authenticated(Roles.read) { implicit request ⇒
+ def describeAll: Action[AnyContent] = authenticated(Roles.read) { _ ⇒
val entityDefinitions = modelSrv
.list
.collect {
diff --git a/thehive-backend/app/controllers/LogCtrl.scala b/thehive-backend/app/controllers/LogCtrl.scala
index 5bb5398e67..ff4879dfd4 100644
--- a/thehive-backend/app/controllers/LogCtrl.scala
+++ b/thehive-backend/app/controllers/LogCtrl.scala
@@ -34,7 +34,7 @@ class LogCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
logSrv
.get(id)
.map(log ⇒ renderer.toOutput(OK, log))
diff --git a/thehive-backend/app/controllers/StatusCtrl.scala b/thehive-backend/app/controllers/StatusCtrl.scala
index 936a029744..63b24b1281 100644
--- a/thehive-backend/app/controllers/StatusCtrl.scala
+++ b/thehive-backend/app/controllers/StatusCtrl.scala
@@ -1,25 +1,24 @@
package controllers
import akka.actor.ActorSystem
-
-import scala.collection.immutable
-import scala.concurrent.ExecutionContext
-import scala.util.Try
-import play.api.Configuration
-import play.api.libs.json.Json.toJsFieldJsValueWrapper
-import play.api.libs.json.{JsBoolean, JsObject, JsString, Json}
-import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
-import com.sksamuel.elastic4s.http.ElasticDsl
+import com.sksamuel.elastic4s.ElasticDsl
import connectors.Connector
import javax.inject.{Inject, Singleton}
import models.HealthStatus
-import org.elasticsearch.client.Node
import org.elastic4play.Timed
import org.elastic4play.database.DBIndex
import org.elastic4play.services.AuthSrv
import org.elastic4play.services.auth.MultiAuthSrv
+import org.elasticsearch.client.Node
+import play.api.Configuration
+import play.api.libs.json.Json.toJsFieldJsValueWrapper
+import play.api.libs.json.{JsBoolean, JsObject, JsString, Json}
+import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
+import scala.collection.immutable
+import scala.concurrent.ExecutionContext
import scala.concurrent.duration.{DurationInt, FiniteDuration}
+import scala.util.Try
@Singleton
class StatusCtrl @Inject()(
diff --git a/thehive-backend/app/controllers/StreamCtrl.scala b/thehive-backend/app/controllers/StreamCtrl.scala
index 7f3ca03a27..756236b735 100644
--- a/thehive-backend/app/controllers/StreamCtrl.scala
+++ b/thehive-backend/app/controllers/StreamCtrl.scala
@@ -1,30 +1,27 @@
package controllers
+import akka.actor.{ActorIdentity, ActorSystem, Identify, Props}
+import akka.cluster.pubsub.DistributedPubSub
+import akka.cluster.pubsub.DistributedPubSubMediator.{Put, Send}
+import akka.pattern.{AskTimeoutException, ask}
+import akka.util.Timeout
import javax.inject.{Inject, Singleton}
-
-import scala.collection.immutable
-import scala.concurrent.{ExecutionContext, Future}
-import scala.concurrent.duration.{DurationLong, FiniteDuration}
-import scala.util.Random
-
+import models.Roles
+import org.elastic4play.Timed
+import org.elastic4play.controllers._
+import org.elastic4play.services.{MigrationSrv, UserSrv}
import play.api.http.Status
import play.api.libs.json.Json
import play.api.libs.json.Json.toJsFieldJsValueWrapper
import play.api.mvc._
import play.api.{Configuration, Logger}
-
-import akka.actor.{ActorIdentity, ActorSystem, Identify, Props}
-import akka.cluster.pubsub.DistributedPubSub
-import akka.cluster.pubsub.DistributedPubSubMediator.{Put, Send}
-import akka.pattern.{ask, AskTimeoutException}
-import akka.util.Timeout
-import models.Roles
import services.StreamActor
import services.StreamActor.StreamMessages
-import org.elastic4play.controllers._
-import org.elastic4play.services.{AuxSrv, EventSrv, MigrationSrv, UserSrv}
-import org.elastic4play.Timed
+import scala.collection.immutable
+import scala.concurrent.duration.{DurationLong, FiniteDuration}
+import scala.concurrent.{ExecutionContext, Future}
+import scala.util.Random
@Singleton
class StreamCtrl(
@@ -32,9 +29,7 @@ class StreamCtrl(
refresh: FiniteDuration,
authenticated: Authenticated,
renderer: Renderer,
- eventSrv: EventSrv,
userSrv: UserSrv,
- auxSrv: AuxSrv,
migrationSrv: MigrationSrv,
components: ControllerComponents,
implicit val system: ActorSystem,
@@ -46,9 +41,7 @@ class StreamCtrl(
configuration: Configuration,
authenticated: Authenticated,
renderer: Renderer,
- eventSrv: EventSrv,
userSrv: UserSrv,
- auxSrv: AuxSrv,
migrationSrv: MigrationSrv,
components: ControllerComponents,
system: ActorSystem,
@@ -59,9 +52,7 @@ class StreamCtrl(
configuration.getMillis("stream.longpolling.refresh").millis,
authenticated,
renderer,
- eventSrv,
userSrv,
- auxSrv,
migrationSrv,
components,
system,
@@ -125,7 +116,7 @@ class StreamCtrl(
}
@Timed("controllers.StreamCtrl.status")
- def status = Action { implicit request ⇒
+ def status: Action[AnyContent] = Action { implicit request ⇒
val status = authenticated.expirationStatus(request) match {
case ExpirationWarning(duration) ⇒ Json.obj("remaining" → duration.toSeconds, "warning" → true)
case ExpirationError ⇒ Json.obj("remaining" → 0, "warning" → true)
diff --git a/thehive-backend/app/controllers/TaskCtrl.scala b/thehive-backend/app/controllers/TaskCtrl.scala
index defb9d4143..730548103e 100644
--- a/thehive-backend/app/controllers/TaskCtrl.scala
+++ b/thehive-backend/app/controllers/TaskCtrl.scala
@@ -36,7 +36,7 @@ class TaskCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
taskSrv
.get(id)
.map(task ⇒ renderer.toOutput(OK, task))
diff --git a/thehive-backend/app/controllers/UserCtrl.scala b/thehive-backend/app/controllers/UserCtrl.scala
index a785317ad1..2965a74bcb 100644
--- a/thehive-backend/app/controllers/UserCtrl.scala
+++ b/thehive-backend/app/controllers/UserCtrl.scala
@@ -41,7 +41,7 @@ class UserCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
userSrv
.get(id)
.map { user ⇒
diff --git a/thehive-backend/app/models/Alert.scala b/thehive-backend/app/models/Alert.scala
index b9afab587f..f45c45db98 100644
--- a/thehive-backend/app/models/Alert.scala
+++ b/thehive-backend/app/models/Alert.scala
@@ -2,32 +2,18 @@ package models
import java.util.Date
-import scala.concurrent.Future
-import scala.util.Try
-
-import play.api.Logger
-import play.api.libs.json._
-
-import javax.inject.{Inject, Singleton}
+import javax.inject.Singleton
import models.JsonFormat.alertStatusFormat
-import services.AuditedModel
-
import org.elastic4play.controllers.JsonInputValue
-import org.elastic4play.models.{
- Attribute,
- AttributeDef,
- BaseEntity,
- EntityDef,
- HiveEnumeration,
- ModelDef,
- MultiAttributeFormat,
- OptionalAttributeFormat,
- AttributeFormat ⇒ F,
- AttributeOption ⇒ O
-}
-import org.elastic4play.services.DBLists
+import org.elastic4play.models.{Attribute, AttributeDef, BaseEntity, EntityDef, HiveEnumeration, ModelDef, MultiAttributeFormat, OptionalAttributeFormat, AttributeFormat => F, AttributeOption => O}
import org.elastic4play.utils.Hasher
import org.elastic4play.{AttributeCheckingError, InvalidFormatAttributeError}
+import play.api.Logger
+import play.api.libs.json._
+import services.AuditedModel
+
+import scala.concurrent.Future
+import scala.util.Try
object AlertStatus extends Enumeration with HiveEnumeration {
type Type = Value
@@ -60,7 +46,7 @@ trait AlertAttributes {
Attribute("alert", "ioc", OptionalAttributeFormat(F.booleanFmt), Nil, None, "")
)
}
-
+
val alertId: A[String] = attribute("_id", F.stringFmt, "Alert id", O.readonly)
val tpe: A[String] = attribute("type", F.stringFmt, "Type of the alert", O.readonly)
val source: A[String] = attribute("source", F.stringFmt, "Source of the alert", O.readonly)
@@ -81,7 +67,7 @@ trait AlertAttributes {
}
@Singleton
-class AlertModel @Inject()(dblists: DBLists) extends ModelDef[AlertModel, Alert]("alert", "Alert", "/alert") with AlertAttributes with AuditedModel {
+class AlertModel extends ModelDef[AlertModel, Alert]("alert", "Alert", "/alert") with AlertAttributes with AuditedModel {
private[AlertModel] lazy val logger = Logger(getClass)
override val defaultSortBy: Seq[String] = Seq("-date")
diff --git a/thehive-backend/app/models/Artifact.scala b/thehive-backend/app/models/Artifact.scala
index 25264ee27d..beb747afaa 100644
--- a/thehive-backend/app/models/Artifact.scala
+++ b/thehive-backend/app/models/Artifact.scala
@@ -102,10 +102,9 @@ class ArtifactModel @Inject()(
mm.addValue((attrs \ "data").asOpt[JsValue].getOrElse(JsNull))
mm.addValue((attrs \ "dataType").asOpt[JsValue].getOrElse(JsNull))
for {
- IOResult(_, done) ← (attrs \ "attachment" \ "filepath")
+ IOResult(_, _) ← (attrs \ "attachment" \ "filepath")
.asOpt[String]
.fold(Future.successful(IOResult(0, Success(Done))))(file ⇒ mm.addFile(file))
- _ ← Future.fromTry(done)
_ ← (attrs \ "attachment" \ "id")
.asOpt[String]
.fold(Future.successful(NotUsed: NotUsed)) { fileId ⇒
diff --git a/thehive-backend/app/models/Audit.scala b/thehive-backend/app/models/Audit.scala
index 6cd670c8eb..5aa0f32b5a 100644
--- a/thehive-backend/app/models/Audit.scala
+++ b/thehive-backend/app/models/Audit.scala
@@ -48,7 +48,7 @@ class AuditModel(auditName: String, auditedModels: immutable.Set[AuditedModel],
extends ModelDef[AuditModel, Audit](auditName, "Audit", "/audit")
with AuditAttributes {
- lazy val auxSrv = auxSrvProvider.get()
+ lazy val auxSrv: AuxSrv = auxSrvProvider.get()
@Inject() def this(
configuration: Configuration,
@@ -134,5 +134,5 @@ class AuditModel(auditName: String, auditedModels: immutable.Set[AuditedModel],
}
class Audit(model: AuditModel, attributes: JsObject) extends EntityDef[AuditModel, Audit](model, attributes) with AuditAttributes {
- def detailsAttributes = Nil
+ def detailsAttributes: Seq[Attribute[_]] = Nil
}
diff --git a/thehive-backend/app/models/CaseTemplate.scala b/thehive-backend/app/models/CaseTemplate.scala
index dc3f607298..36d941f9ee 100644
--- a/thehive-backend/app/models/CaseTemplate.scala
+++ b/thehive-backend/app/models/CaseTemplate.scala
@@ -44,5 +44,5 @@ class CaseTemplateModel @Inject()(taskModel: TaskModel)
class CaseTemplate(model: CaseTemplateModel, attributes: JsObject)
extends EntityDef[CaseTemplateModel, CaseTemplate](model, attributes)
with CaseTemplateAttributes {
- def taskAttributes = Nil
+ def taskAttributes: Seq[Attribute[_]] = Nil
}
diff --git a/thehive-backend/app/models/Log.scala b/thehive-backend/app/models/Log.scala
index 611ac378ac..b8e77d5d2e 100644
--- a/thehive-backend/app/models/Log.scala
+++ b/thehive-backend/app/models/Log.scala
@@ -35,7 +35,7 @@ class LogModel @Inject()(taskModel: TaskModel)
extends ChildModelDef[LogModel, Log, TaskModel, Task](taskModel, "case_task_log", "Log", "/case/task/log")
with LogAttributes
with AuditedModel {
- override val defaultSortBy = Seq("-startDate")
- override val removeAttribute = Json.obj("status" → LogStatus.Deleted)
+ override val defaultSortBy: Seq[String] = Seq("-startDate")
+ override val removeAttribute: JsObject = Json.obj("status" → LogStatus.Deleted)
}
class Log(model: LogModel, attributes: JsObject) extends EntityDef[LogModel, Log](model, attributes) with LogAttributes
diff --git a/thehive-backend/app/models/Migration.scala b/thehive-backend/app/models/Migration.scala
index ae6a65ce22..febdd6e875 100644
--- a/thehive-backend/app/models/Migration.scala
+++ b/thehive-backend/app/models/Migration.scala
@@ -3,26 +3,24 @@ package models
import java.nio.file.{Files, Path}
import java.util.Date
-import scala.collection.JavaConverters._
-import scala.concurrent.{ExecutionContext, Future}
-import scala.math.BigDecimal.int2bigDecimal
-import scala.util.Try
-
-import play.api.libs.json.JsValue.jsValueToJsLookup
-import play.api.libs.json._
-import play.api.{Configuration, Environment, Logger}
-
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import javax.inject.{Inject, Singleton}
-import services.{AlertSrv, DashboardSrv}
-
import org.elastic4play.ConflictError
import org.elastic4play.controllers.Fields
import org.elastic4play.services.JsonFormat.attachmentFormat
-import org.elastic4play.services.{IndexType, _}
+import org.elastic4play.services._
import org.elastic4play.utils.Hasher
+import play.api.libs.json.JsValue.jsValueToJsLookup
+import play.api.libs.json._
+import play.api.{Configuration, Environment, Logger}
+import services.{AlertSrv, DashboardSrv}
+
+import scala.collection.JavaConverters._
+import scala.concurrent.{ExecutionContext, Future}
+import scala.math.BigDecimal.int2bigDecimal
+import scala.util.Try
case class UpdateMispAlertArtifact() extends EventMessage
@@ -143,8 +141,6 @@ class Migration(
}
- override def indexType(version: Int): IndexType.Value = if (version > 14) IndexType.indexWithoutMappingTypes else IndexType.indexWithMappingTypes
-
override val operations: PartialFunction[DatabaseState, Seq[Operation]] = {
case DatabaseState(version) if version < 7 ⇒ Nil
case DatabaseState(7) ⇒
@@ -248,7 +244,7 @@ class Migration(
dataStr ← (artifact \ "data").asOpt[String]
dataJson ← Try(Json.parse(dataStr)).toOption
dataObj ← dataJson.asOpt[JsObject]
- filename ← (dataObj \ "filename").asOpt[String].map(_.split("|").head)
+ filename ← (dataObj \ "filename").asOpt[String].map(_.split("\\|").head)
attributeId ← (dataObj \ "attributeId").asOpt[String]
attributeType ← (dataObj \ "attributeType").asOpt[String]
} yield Future.successful(
@@ -280,7 +276,7 @@ class Migration(
}
}
Source
- .fromFuture(artifactsAndData)
+ .future(artifactsAndData)
.mapConcat { ad ⇒
val updatedAlert = alert + ("artifacts" → JsArray(ad.map(_._1)))
updatedAlert :: ad.flatMap(_._2)
@@ -355,7 +351,7 @@ class Migration(
audit
}
}
- Source.fromFuture(updatedAudit)
+ Source.future(updatedAudit)
case audit ⇒ Source.single(audit)
}
case other ⇒ f(other)
@@ -378,6 +374,7 @@ class Migration(
("sequenceCounter" → counter)
}
)
+ case DatabaseState(15) ⇒ Nil
}
private def generateAlertId(alert: JsObject): String = {
diff --git a/thehive-backend/app/models/Roles.scala b/thehive-backend/app/models/Roles.scala
index 393b22cc67..9e6e574b31 100644
--- a/thehive-backend/app/models/Roles.scala
+++ b/thehive-backend/app/models/Roles.scala
@@ -1,16 +1,14 @@
package models
-import play.api.libs.json.{JsString, JsValue}
-
-import com.sksamuel.elastic4s.http.ElasticDsl.keywordField
-import com.sksamuel.elastic4s.mappings.KeywordField
-import org.scalactic.{Every, Good, One, Or}
+import com.sksamuel.elastic4s.ElasticDsl.keywordField
+import com.sksamuel.elastic4s.requests.mappings.KeywordField
import models.JsonFormat.roleFormat
-
-import org.elastic4play.{AttributeError, InvalidFormatAttributeError}
import org.elastic4play.controllers.{InputValue, JsonInputValue, StringInputValue}
import org.elastic4play.models.AttributeFormat
import org.elastic4play.services.Role
+import org.elastic4play.{AttributeError, InvalidFormatAttributeError}
+import org.scalactic.{Every, Good, One, Or}
+import play.api.libs.json.{JsString, JsValue}
object Roles {
object read extends Role("read")
diff --git a/thehive-backend/app/models/User.scala b/thehive-backend/app/models/User.scala
index 97fd563407..8c88d3465c 100644
--- a/thehive-backend/app/models/User.scala
+++ b/thehive-backend/app/models/User.scala
@@ -1,15 +1,12 @@
package models
import scala.concurrent.Future
-
import play.api.libs.json.JsValue.jsValueToJsLookup
import play.api.libs.json._
-
import models.JsonFormat.userStatusFormat
import services.AuditedModel
-
-import org.elastic4play.models.{AttributeDef, BaseEntity, EntityDef, HiveEnumeration, ModelDef, AttributeFormat ⇒ F, AttributeOption ⇒ O}
-import org.elastic4play.services.{User ⇒ EUser}
+import org.elastic4play.models.{AttributeDef, BaseEntity, EntityDef, HiveEnumeration, ModelDef, AttributeFormat => F, AttributeOption => O}
+import org.elastic4play.services.{Role, User => EUser}
object UserStatus extends Enumeration with HiveEnumeration {
type Type = Value
@@ -40,8 +37,8 @@ class UserModel extends ModelDef[UserModel, User]("user", "User", "/user") with
}
class User(model: UserModel, attributes: JsObject) extends EntityDef[UserModel, User](model, attributes) with UserAttributes with EUser {
- override def getUserName = userName()
- override def getRoles = roles()
+ override def getUserName: String = userName()
+ override def getRoles: Seq[Role] = roles()
override def toJson: JsObject =
super.toJson +
diff --git a/thehive-backend/app/models/package.scala b/thehive-backend/app/models/package.scala
index 5817120386..92fd3669a9 100644
--- a/thehive-backend/app/models/package.scala
+++ b/thehive-backend/app/models/package.scala
@@ -1,3 +1,3 @@
package object models {
- val modelVersion = 15
+ val modelVersion = 16
}
diff --git a/thehive-backend/app/services/AlertSrv.scala b/thehive-backend/app/services/AlertSrv.scala
index 79755cd5c3..952b5719dd 100644
--- a/thehive-backend/app/services/AlertSrv.scala
+++ b/thehive-backend/app/services/AlertSrv.scala
@@ -39,7 +39,6 @@ object AlertSrv {
@Singleton
class AlertSrv(
- templates: Map[String, String],
alertModel: AlertModel,
createSrv: CreateSrv,
getSrv: GetSrv,
@@ -51,7 +50,6 @@ class AlertSrv(
caseTemplateSrv: CaseTemplateSrv,
attachmentSrv: AttachmentSrv,
connectors: ConnectorRouter,
- hashAlg: Seq[String],
implicit val ec: ExecutionContext,
implicit val mat: Materializer
) extends AlertTransformer {
@@ -73,7 +71,6 @@ class AlertSrv(
mat: Materializer
) =
this(
- Map.empty[String, String],
alertModel: AlertModel,
createSrv,
getSrv,
@@ -85,7 +82,6 @@ class AlertSrv(
caseTemplateSrv,
attachmentSrv,
connectors,
- (configuration.get[String]("datastore.hash.main") +: configuration.get[Seq[String]]("datastore.hash.extra")).distinct,
ec,
mat
)
@@ -204,7 +200,7 @@ class AlertSrv(
case None ⇒ updateSrv[AlertModel, Alert](alertModel, alert.id, Fields.empty.set("status", "New"), modifyConfig)
}
- def getCaseTemplate(alert: Alert, customCaseTemplate: Option[String]): Future[Option[CaseTemplate]] =
+ def getCaseTemplate(customCaseTemplate: Option[String]): Future[Option[CaseTemplate]] =
customCaseTemplate.fold[Future[Option[CaseTemplate]]](Future.successful(None)) { templateName ⇒
caseTemplateSrv
.getByName(templateName)
@@ -226,7 +222,7 @@ class AlertSrv(
} yield caze
case _ ⇒
for {
- caseTemplate ← getCaseTemplate(alert, customCaseTemplate)
+ caseTemplate ← getCaseTemplate(customCaseTemplate)
caze ← caseSrv.create(
Fields
.empty
diff --git a/thehive-backend/app/services/AuditSrv.scala b/thehive-backend/app/services/AuditSrv.scala
index bd4c147c24..ee7e9e380b 100644
--- a/thehive-backend/app/services/AuditSrv.scala
+++ b/thehive-backend/app/services/AuditSrv.scala
@@ -24,7 +24,7 @@ trait AuditedModel { self: BaseModelDef ⇒
lazy val auditedAttributes: Map[String, Attribute[_]] =
attributes.collect { case a if !a.isUnaudited ⇒ a.attributeName → a }.toMap
- def selectAuditedAttributes(attrs: JsObject) = JsObject {
+ def selectAuditedAttributes(attrs: JsObject): JsObject = JsObject {
attrs.fields.flatMap {
case (attrName, value) ⇒
val attrNames = attrName.split("\\.").toSeq
@@ -102,7 +102,7 @@ class AuditActor @Inject()(auditModel: AuditModel, createSrv: CreateSrv, eventSr
extends Actor {
object EntityExtractor {
- def unapply(e: BaseEntity) = Some((e.model, e.id, e.routing))
+ def unapply(e: BaseEntity): Some[(BaseModelDef, String, String)] = Some((e.model, e.id, e.routing))
}
var currentRequestIds = Set.empty[String]
private[AuditActor] lazy val logger = Logger(getClass)
diff --git a/thehive-backend/app/services/CustomWSAPI.scala b/thehive-backend/app/services/CustomWSAPI.scala
index ae57ec135b..f27e5a066d 100644
--- a/thehive-backend/app/services/CustomWSAPI.scala
+++ b/thehive-backend/app/services/CustomWSAPI.scala
@@ -60,7 +60,7 @@ object CustomWSAPI {
.trustManagerConfig
.withTrustStoreConfigs(
clientConfig.wsClientConfig.ssl.trustManagerConfig.trustStoreConfigs :+ TrustStoreConfig(
- filePath = Some(p.toString),
+ filePath = Some(p),
data = None
)
)
diff --git a/thehive-backend/app/services/OAuth2Srv.scala b/thehive-backend/app/services/OAuth2Srv.scala
index 936a6ad678..d69865369d 100644
--- a/thehive-backend/app/services/OAuth2Srv.scala
+++ b/thehive-backend/app/services/OAuth2Srv.scala
@@ -1,18 +1,19 @@
package services
-import javax.inject.{Inject, Singleton}
+import java.util.UUID
import akka.stream.Materializer
+import javax.inject.{Inject, Singleton}
import org.elastic4play.services.{AuthContext, AuthSrv}
-import org.elastic4play.{AuthenticationError, AuthorizationError, OAuth2Redirect}
-import play.api.http.Status
-import play.api.libs.json.{JsObject, JsValue}
+import org.elastic4play.{AuthenticationError, BadRequestError, NotFoundError}
+import play.api.libs.json.JsObject
import play.api.libs.ws.WSClient
-import play.api.mvc.RequestHeader
+import play.api.mvc.{RequestHeader, Result, Results}
import play.api.{Configuration, Logger}
import services.mappers.UserMapper
import scala.concurrent.{ExecutionContext, Future}
+import scala.util.{Failure, Success}
case class OAuth2Config(
clientId: String,
@@ -24,25 +25,27 @@ case class OAuth2Config(
tokenUrl: String,
userUrl: String,
scope: String,
- autocreate: Boolean,
- autoupdate: Boolean
+ authorizationHeader: String,
+ autoupdate: Boolean,
+ autocreate: Boolean
)
object OAuth2Config {
def apply(configuration: Configuration): Option[OAuth2Config] =
for {
- clientId ← configuration.getOptional[String]("auth.oauth2.clientId")
- clientSecret ← configuration.getOptional[String]("auth.oauth2.clientSecret")
- redirectUri ← configuration.getOptional[String]("auth.oauth2.redirectUri")
- responseType ← configuration.getOptional[String]("auth.oauth2.responseType")
- grantType ← configuration.getOptional[String]("auth.oauth2.grantType")
+ clientId ← configuration.getOptional[String]("auth.oauth2.clientId")
+ clientSecret ← configuration.getOptional[String]("auth.oauth2.clientSecret")
+ redirectUri ← configuration.getOptional[String]("auth.oauth2.redirectUri")
+ responseType ← configuration.getOptional[String]("auth.oauth2.responseType")
+ grantType = configuration.getOptional[String]("auth.oauth2.grantType").getOrElse("authorization_code")
authorizationUrl ← configuration.getOptional[String]("auth.oauth2.authorizationUrl")
- userUrl ← configuration.getOptional[String]("auth.oauth2.userUrl")
tokenUrl ← configuration.getOptional[String]("auth.oauth2.tokenUrl")
+ userUrl ← configuration.getOptional[String]("auth.oauth2.userUrl")
scope ← configuration.getOptional[String]("auth.oauth2.scope")
- autocreate = configuration.getOptional[Boolean]("auth.sso.autocreate").getOrElse(false)
- autoupdate = configuration.getOptional[Boolean]("auth.sso.autoupdate").getOrElse(false)
+ authorizationHeader = configuration.getOptional[String]("auth.oauth2.authorizationHeader").getOrElse("Bearer")
+ autocreate = configuration.getOptional[Boolean]("auth.sso.autocreate").getOrElse(false)
+ autoupdate = configuration.getOptional[Boolean]("auth.sso.autoupdate").getOrElse(false)
} yield OAuth2Config(
clientId,
clientSecret,
@@ -53,6 +56,7 @@ object OAuth2Config {
tokenUrl,
userUrl,
scope,
+ authorizationHeader,
autocreate,
autoupdate
)
@@ -79,104 +83,146 @@ class OAuth2Srv(
private def withOAuth2Config[A](body: OAuth2Config ⇒ Future[A]): Future[A] =
oauth2Config.fold[Future[A]](Future.failed(AuthenticationError("OAuth2 not configured properly")))(body)
- override def authenticate()(implicit request: RequestHeader): Future[AuthContext] =
- withOAuth2Config { cfg ⇒
- request
- .queryString
- .get(Oauth2TokenQueryString)
- .flatMap(_.headOption)
- .fold(createOauth2Redirect(cfg.clientId)) { code ⇒
- getAuthTokenAndAuthenticate(cfg.clientId, code)
+ override def authenticate()(implicit request: RequestHeader): Future[Either[Result, AuthContext]] =
+ withOAuth2Config { oauth2Config ⇒
+ if (!isSecuredAuthCode(request)) {
+ logger.debug("Code or state is not provided, redirect to authorizationUrl")
+ Future.successful(Left(authRedirect(oauth2Config)))
+ } else {
+ (for {
+ token ← getToken(oauth2Config, request)
+ userData ← getUserData(oauth2Config, token)
+ authContext ← authenticate(oauth2Config, request, userData)
+ } yield Right(authContext)).recoverWith {
+ case error ⇒ Future.failed(AuthenticationError(s"OAuth2 authentication failure: ${error.getMessage}"))
}
+ }
}
- private def getAuthTokenAndAuthenticate(clientId: String, code: String)(implicit request: RequestHeader): Future[AuthContext] = {
- logger.debug("Getting user token with the code from the response")
- withOAuth2Config { cfg ⇒
- ws.url(cfg.tokenUrl)
- .post(
- Map(
- "code" → code,
- "grant_type" → cfg.grantType,
- "client_secret" → cfg.clientSecret,
- "redirect_uri" → cfg.redirectUri,
- "client_id" → clientId
- )
- )
- .recoverWith {
- case error ⇒
- logger.error(s"Token verification failure", error)
- Future.failed(AuthenticationError("Token verification failure"))
- }
- .flatMap { r ⇒
- r.status match {
- case Status.OK ⇒
- logger.debug("Getting user info using access token")
- val accessToken = (r.json \ "access_token").asOpt[String].getOrElse("")
- val authHeader = "Authorization" → s"Bearer $accessToken"
- ws.url(cfg.userUrl)
- .addHttpHeaders(authHeader)
- .get()
- .flatMap { userResponse ⇒
- if (userResponse.status != Status.OK) {
- Future.failed(AuthenticationError(s"Unexpected response from server: ${userResponse.status} ${userResponse.body}"))
- } else {
- val response = userResponse.json.asInstanceOf[JsObject]
- getOrCreateUser(response, authHeader)
- }
- }
- case _ ⇒
- logger.error(s"Unexpected response from server: ${r.status} ${r.body}")
- Future.failed(AuthenticationError("Unexpected response from server"))
- }
- }
- }
+ private def isSecuredAuthCode(request: RequestHeader): Boolean =
+ request.queryString.contains("code") && request.queryString.contains("state")
+
+ /**
+ * Filter checking whether we initiate the OAuth2 process
+ * and redirecting to OAuth2 server if necessary
+ * @return
+ */
+ private def authRedirect(oauth2Config: OAuth2Config): Result = {
+ val state = UUID.randomUUID().toString
+ val queryStringParams = Map[String, Seq[String]](
+ "scope" → Seq(oauth2Config.scope),
+ "response_type" → Seq(oauth2Config.responseType),
+ "redirect_uri" → Seq(oauth2Config.redirectUri),
+ "client_id" → Seq(oauth2Config.clientId),
+ "state" → Seq(state)
+ )
+
+ logger.debug(s"Redirecting to ${oauth2Config.redirectUri} with $queryStringParams and state $state")
+ Results
+ .Redirect(oauth2Config.authorizationUrl, queryStringParams, status = 302)
+ .withSession("state" → state)
}
- private def getOrCreateUser(response: JsValue, authHeader: (String, String))(implicit request: RequestHeader): Future[AuthContext] =
- withOAuth2Config { cfg ⇒
- ssoMapper.getUserFields(response, Some(authHeader)).flatMap { userFields ⇒
- val userId = userFields.getString("login").getOrElse("")
- userSrv
- .get(userId)
- .flatMap(user ⇒ {
- if (cfg.autoupdate) {
- logger.debug(s"Updating OAuth/OIDC user")
- userSrv.inInitAuthContext { implicit authContext ⇒
- // Only update name and roles, not login (can't change it)
- userSrv
- .update(user, userFields.unset("login"))
- .flatMap(user ⇒ {
- userSrv.getFromUser(request, user, name)
- })
- }
- } else {
- userSrv.getFromUser(request, user, name)
+ /**
+ * Enriching the initial request with OAuth2 token gotten
+ * from OAuth2 code
+ * @return
+ */
+ private def getToken[A](oauth2Config: OAuth2Config, request: RequestHeader): Future[String] = {
+ val token =
+ for {
+ state ← request.session.get("state")
+ stateQs ← request.queryString.get("state").flatMap(_.headOption)
+ if state == stateQs
+ } yield request.queryString.get("code").flatMap(_.headOption) match {
+ case Some(code) ⇒
+ logger.debug(s"Attempting to retrieve OAuth2 token from ${oauth2Config.tokenUrl} with code $code")
+ getAuthTokenFromCode(oauth2Config, code, state)
+ .map { t ⇒
+ logger.trace(s"Got token $t")
+ t
}
- })
- .recoverWith {
- case authErr: AuthorizationError ⇒ Future.failed(authErr)
- case _ if cfg.autocreate ⇒
- logger.debug(s"Creating OAuth/OIDC user")
- userSrv.inInitAuthContext { implicit authContext ⇒
- userSrv
- .create(userFields)
- .flatMap(user ⇒ {
- userSrv.getFromUser(request, user, name)
- })
- }
- }
+ case None ⇒
+ Future.failed(AuthenticationError(s"OAuth2 server code missing ${request.queryString.get("error")}"))
}
- }
+ token.getOrElse(Future.failed(BadRequestError("OAuth2 states mismatch")))
+ }
- private def createOauth2Redirect(clientId: String): Future[AuthContext] =
- withOAuth2Config { cfg ⇒
- val queryStringParams = Map[String, Seq[String]](
- "scope" → Seq(cfg.scope),
- "response_type" → Seq(cfg.responseType),
- "redirect_uri" → Seq(cfg.redirectUri),
- "client_id" → Seq(clientId)
+ /**
+ * Querying the OAuth2 server for a token
+ * @param code the previously obtained code
+ * @return
+ */
+ private def getAuthTokenFromCode(oauth2Config: OAuth2Config, code: String, state: String): Future[String] = {
+ logger.trace(s"""
+ |Request to ${oauth2Config.tokenUrl} with
+ | code: $code
+ | grant_type: ${oauth2Config.grantType}
+ | client_secret: ${oauth2Config.clientSecret}
+ | redirect_uri: ${oauth2Config.redirectUri}
+ | client_id: ${oauth2Config.clientId}
+ | state: $state
+ |""".stripMargin)
+ ws.url(oauth2Config.tokenUrl)
+ .withHttpHeaders("Accept" → "application/json")
+ .post(
+ Map(
+ "code" → code,
+ "grant_type" → oauth2Config.grantType,
+ "client_secret" → oauth2Config.clientSecret,
+ "redirect_uri" → oauth2Config.redirectUri,
+ "client_id" → oauth2Config.clientId,
+ "state" → state
+ )
)
- Future.failed(OAuth2Redirect(cfg.authorizationUrl, queryStringParams))
- }
+ .transform {
+ case Success(r) if r.status == 200 ⇒ Success((r.json \ "access_token").asOpt[String].getOrElse(""))
+ case Failure(error) ⇒ Failure(AuthenticationError(s"OAuth2 token verification failure ${error.getMessage}"))
+ case Success(r) ⇒ Failure(AuthenticationError(s"OAuth2/token unexpected response from server (${r.status} ${r.statusText})"))
+ }
+ }
+
+ /**
+ * Client query for user data with OAuth2 token
+ * @param token the token
+ * @return
+ */
+ private def getUserData(oauth2Config: OAuth2Config, token: String): Future[JsObject] = {
+ logger.trace(s"Request to ${oauth2Config.userUrl} with authorization header: ${oauth2Config.authorizationHeader} $token")
+ ws.url(oauth2Config.userUrl)
+ .addHttpHeaders("Authorization" → s"${oauth2Config.authorizationHeader} $token")
+ .get()
+ .transform {
+ case Success(r) if r.status == 200 ⇒ Success(r.json.as[JsObject])
+ case Failure(error) ⇒ Failure(AuthenticationError(s"OAuth2 user data fetch failure ${error.getMessage}"))
+ case Success(r) ⇒ Failure(AuthenticationError(s"OAuth2/userinfo unexpected response from server (${r.status} ${r.statusText})"))
+ }
+ }
+
+ private def authenticate(oauth2Config: OAuth2Config, request: RequestHeader, userData: JsObject): Future[AuthContext] =
+ for {
+ userFields ← ssoMapper.getUserFields(userData)
+ login ← userFields.getString("login").fold(Future.failed[String](AuthenticationError("")))(Future.successful)
+ user ← userSrv
+ .get(login)
+ .flatMap {
+ case u if oauth2Config.autoupdate ⇒
+ logger.debug(s"Updating OAuth/OIDC user")
+ userSrv.inInitAuthContext { implicit authContext ⇒
+ // Only update name and roles, not login (can't change it)
+ userSrv
+ .update(u, userFields.unset("login"))
+
+ }
+ case u ⇒ Future.successful(u)
+ }
+ .recoverWith {
+ case _: NotFoundError if oauth2Config.autocreate ⇒
+ logger.debug(s"Creating OAuth/OIDC user")
+ userSrv.inInitAuthContext { implicit authContext ⇒
+ userSrv.create(userFields.set("login", userFields.getString("login").get.toLowerCase))
+ }
+ }
+ authContext ← userSrv.getFromUser(request, user, name)
+ } yield authContext
}
diff --git a/thehive-backend/app/services/StreamMessage.scala b/thehive-backend/app/services/StreamMessage.scala
index 57b87d1e0b..db679edb6a 100644
--- a/thehive-backend/app/services/StreamMessage.scala
+++ b/thehive-backend/app/services/StreamMessage.scala
@@ -32,7 +32,7 @@ case class AggregatedAuditMessage(auxSrv: AuxSrv, message: Future[JsObject], sum
}
object AggregatedAuditMessage {
- lazy val logger = Logger(getClass)
+ lazy val logger: Logger = Logger(getClass)
def apply(auxSrv: AuxSrv, operation: AuditOperation)(implicit ec: ExecutionContext): AggregatedAuditMessage = {
// First operation of the group
diff --git a/thehive-backend/app/services/StreamSrv.scala b/thehive-backend/app/services/StreamSrv.scala
index b293f18c39..6cca5fb54b 100644
--- a/thehive-backend/app/services/StreamSrv.scala
+++ b/thehive-backend/app/services/StreamSrv.scala
@@ -29,7 +29,7 @@ object StreamActor {
case class StreamMessages(messages: Seq[JsObject]) extends StreamActorMessage
object StreamMessages {
- val empty = StreamMessages(Nil)
+ val empty: StreamMessages = StreamMessages(Nil)
}
}
diff --git a/thehive-backend/app/services/TheHiveAuthSrv.scala b/thehive-backend/app/services/TheHiveAuthSrv.scala
index e66c783cdc..a49224aedb 100644
--- a/thehive-backend/app/services/TheHiveAuthSrv.scala
+++ b/thehive-backend/app/services/TheHiveAuthSrv.scala
@@ -29,7 +29,6 @@ object TheHiveAuthSrv {
class TheHiveAuthSrv @Inject()(
configuration: Configuration,
authModules: immutable.Set[AuthSrv],
- userSrv: UserSrv,
implicit override val ec: ExecutionContext
) extends MultiAuthSrv(
TheHiveAuthSrv.getAuthSrv(configuration.getDeprecated[Option[Seq[String]]]("auth.provider", "auth.type").getOrElse(Seq("local")), authModules),
diff --git a/thehive-backend/app/services/mappers/GroupUserMapper.scala b/thehive-backend/app/services/mappers/GroupUserMapper.scala
index cf036ce379..1d3817738f 100644
--- a/thehive-backend/app/services/mappers/GroupUserMapper.scala
+++ b/thehive-backend/app/services/mappers/GroupUserMapper.scala
@@ -4,14 +4,14 @@ import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
import scala.util.parsing.combinator._
-
import play.api.{Configuration, Logger}
import play.api.libs.json._
import play.api.libs.ws.WSClient
-
import org.elastic4play.{AuthenticationError, AuthorizationError}
import org.elastic4play.controllers.Fields
+import scala.util.matching.Regex
+
class GroupUserMapper(
loginAttrName: String,
nameAttrName: String,
@@ -25,7 +25,7 @@ class GroupUserMapper(
@Inject() def this(configuration: Configuration, ws: WSClient, ec: ExecutionContext) =
this(
- configuration.getOptional[String]("auth.sso.attributes.login").getOrElse("sub"),
+ configuration.getOptional[String]("auth.sso.attributes.login").getOrElse("login"),
configuration.getOptional[String]("auth.sso.attributes.name").getOrElse("name"),
configuration.getOptional[String]("auth.sso.attributes.groups").getOrElse(""),
configuration.getOptional[Seq[String]]("auth.sso.defaultRoles").getOrElse(Seq()),
@@ -40,60 +40,57 @@ class GroupUserMapper(
private[GroupUserMapper] lazy val logger = Logger(getClass)
private class RoleListParser extends RegexParsers {
- val str = "[a-zA-Z0-9_]+".r
- val strSpc = "[a-zA-Z0-9_ ]+".r
- val realStr = ("\""~>strSpc<~"\"" | "'"~>strSpc<~"'" | str)
+ val str: Regex = "[a-zA-Z0-9_]+".r
+ val strSpc: Regex = "[a-zA-Z0-9_ ]+".r
+ val realStr: Parser[String] = "\"" ~> strSpc <~ "\"" | "'" ~> strSpc <~ "'" | str
- def expr: Parser[Seq[String]] = {
+ def expr: Parser[Seq[String]] =
"[" ~ opt(realStr ~ rep("," ~ realStr)) ~ "]" ^^ {
- case _ ~ Some(firstRole ~ list) ~ _ ⇒ list.foldLeft(Seq(firstRole)) {
- case (queue, _ ~ role) ⇒ role +: queue
- }
- case _ ~ _ ⇒ Seq.empty[String]
+ case _ ~ Some(firstRole ~ list) ~ _ ⇒
+ list.foldLeft(Seq(firstRole)) {
+ case (queue, _ ~ role) ⇒ role +: queue
+ }
+ case _ ~ _ ⇒ Seq.empty[String]
} | opt(realStr) ^^ {
case Some(role) ⇒ Seq(role)
case None ⇒ Seq.empty[String]
}
- }
}
- override def getUserFields(jsValue: JsValue, authHeader: Option[(String, String)]): Future[Fields] = {
+ override def getUserFields(jsValue: JsValue, authHeader: Option[(String, String)]): Future[Fields] =
groupsUrl match {
- case Some(groupsEndpointUrl) ⇒ {
- logger.debug(s"Retreiving groups from ${groupsEndpointUrl}")
+ case Some(groupsEndpointUrl) ⇒
+ logger.debug(s"Retreiving groups from $groupsEndpointUrl")
val apiCall = authHeader.fold(ws.url(groupsEndpointUrl))(headers ⇒ ws.url(groupsEndpointUrl).addHttpHeaders(headers))
- apiCall.get.flatMap { r ⇒ extractGroupsThenBuildUserFields(jsValue, r.json) }
- }
- case None ⇒ {
+ apiCall.get.flatMap { r ⇒
+ extractGroupsThenBuildUserFields(jsValue, r.json)
+ }
+ case None ⇒
logger.debug(s"Extracting groups from user info")
extractGroupsThenBuildUserFields(jsValue, jsValue)
- }
}
- }
- private def extractGroupsThenBuildUserFields(jsValue: JsValue, groupsContainer: JsValue): Future[Fields] = {
- (groupsContainer \ groupsAttrName) match {
+ private def extractGroupsThenBuildUserFields(jsValue: JsValue, groupsContainer: JsValue): Future[Fields] =
+ groupsContainer \ groupsAttrName match {
// Groups received as valid JSON array
case JsDefined(JsArray(groupsList)) ⇒ mapGroupsAndBuildUserFields(jsValue, groupsList.map(_.as[String]).toList)
// Groups list received as string (invalid JSON, for example: "ROLE" or "['Role 1', ROLE2, 'Role_3']")
- case JsDefined(JsString(groupsStr)) ⇒ {
+ case JsDefined(JsString(groupsStr)) ⇒
val parser = new RoleListParser
parser.parseAll(parser.expr, groupsStr) match {
case parser.Success(result, _) ⇒ mapGroupsAndBuildUserFields(jsValue, result)
case err: parser.NoSuccess ⇒ Future.failed(AuthenticationError(s"User info fails: can't parse groups list (${err.msg})"))
}
- }
// Invalid group list
case JsDefined(error) ⇒
- Future.failed(AuthenticationError(s"User info fails: invalid groups list received in user info ('${error}' of type ${error.getClass})"))
+ Future.failed(AuthenticationError(s"User info fails: invalid groups list received in user info ('$error' of type ${error.getClass})"))
// Groups field is undefined
case _: JsUndefined ⇒
- Future.failed(AuthenticationError(s"User info fails: groups attribute ${groupsAttrName} doesn't exist in user info"))
+ Future.failed(AuthenticationError(s"User info fails: groups attribute $groupsAttrName doesn't exist in user info"))
}
- }
private def mapGroupsAndBuildUserFields(jsValue: JsValue, jsonGroups: Seq[String]): Future[Fields] = {
val mappedRoles = jsonGroups.flatMap(mappings.get).flatten.toSet
@@ -111,7 +108,8 @@ class GroupUserMapper(
} yield Fields(Json.obj("login" → login, "name" → name, "roles" → roles))
fields match {
case JsSuccess(f, _) ⇒ Future.successful(f)
- case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}"))
+ case JsError(errors) ⇒
+ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}"))
}
}
}
diff --git a/thehive-backend/app/services/mappers/SimpleUserMapper.scala b/thehive-backend/app/services/mappers/SimpleUserMapper.scala
index 598d9d2ece..7389909c11 100644
--- a/thehive-backend/app/services/mappers/SimpleUserMapper.scala
+++ b/thehive-backend/app/services/mappers/SimpleUserMapper.scala
@@ -20,7 +20,7 @@ class SimpleUserMapper(
@Inject() def this(configuration: Configuration, ec: ExecutionContext) =
this(
- configuration.getOptional[String]("auth.sso.attributes.login").getOrElse("sub"),
+ configuration.getOptional[String]("auth.sso.attributes.login").getOrElse("login"),
configuration.getOptional[String]("auth.sso.attributes.name").getOrElse("name"),
configuration.getOptional[String]("auth.sso.attributes.roles"),
configuration.getOptional[Seq[String]]("auth.sso.defaultRoles").getOrElse(Seq()),
@@ -37,7 +37,8 @@ class SimpleUserMapper(
} yield Fields(Json.obj("login" → login, "name" → name, "roles" → roles))
fields match {
case JsSuccess(f, _) ⇒ Future.successful(f)
- case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}"))
+ case JsError(errors) ⇒
+ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}"))
}
}
}
diff --git a/thehive-backend/conf/routes b/thehive-backend/conf/routes
index f0acbc4687..420300c830 100644
--- a/thehive-backend/conf/routes
+++ b/thehive-backend/conf/routes
@@ -7,6 +7,7 @@ GET /api/status controllers.StatusCtrl.get
GET /api/health controllers.StatusCtrl.health
GET /api/logout controllers.AuthenticationCtrl.logout()
POST /api/login controllers.AuthenticationCtrl.login()
+GET /api/ssoLogin controllers.AuthenticationCtrl.ssoLogin()
POST /api/ssoLogin controllers.AuthenticationCtrl.ssoLogin()
POST /api/_search controllers.SearchCtrl.find()
diff --git a/thehive-cortex/app/connectors/cortex/CortexConnector.scala b/thehive-cortex/app/connectors/cortex/CortexConnector.scala
index 11ea0e0856..7d25dd9749 100644
--- a/thehive-cortex/app/connectors/cortex/CortexConnector.scala
+++ b/thehive-cortex/app/connectors/cortex/CortexConnector.scala
@@ -1,13 +1,12 @@
package connectors.cortex
-import play.api.libs.concurrent.AkkaGuiceSupport
-import play.api.{Configuration, Environment, Logger}
-
import connectors.ConnectorModule
import connectors.cortex.controllers.CortexCtrl
import connectors.cortex.services.JobReplicateActor
+import play.api.Logger
+import play.api.libs.concurrent.AkkaGuiceSupport
-class CortexConnector(environment: Environment, configuration: Configuration) extends ConnectorModule with AkkaGuiceSupport {
+class CortexConnector extends ConnectorModule with AkkaGuiceSupport {
private[CortexConnector] lazy val logger = Logger(getClass)
override def configure() {
diff --git a/thehive-cortex/app/connectors/cortex/controllers/CortexCtrl.scala b/thehive-cortex/app/connectors/cortex/controllers/CortexCtrl.scala
index 64591954ed..c0c463a689 100644
--- a/thehive-cortex/app/connectors/cortex/controllers/CortexCtrl.scala
+++ b/thehive-cortex/app/connectors/cortex/controllers/CortexCtrl.scala
@@ -3,21 +3,18 @@ package connectors.cortex.controllers
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
-
import play.api.{Configuration, Logger}
import play.api.http.Status
import play.api.libs.json.{JsObject, Json}
import play.api.mvc._
-import play.api.routing.SimpleRouter
+import play.api.routing.{Router, SimpleRouter}
import play.api.routing.sird.{DELETE, GET, PATCH, POST, UrlContext}
-
import akka.actor.ActorSystem
import connectors.Connector
import connectors.cortex.models.JsonFormat.{analyzerFormat, responderFormat}
import connectors.cortex.services.{CortexActionSrv, CortexAnalyzerSrv, CortexConfig}
import javax.inject.{Inject, Singleton}
import models.{HealthStatus, Roles}
-
import org.elastic4play.controllers.{Authenticated, Fields, FieldsBodyParser, Renderer}
import org.elastic4play.models.JsonFormat.baseModelEntityWrites
import org.elastic4play.services.JsonFormat.{aggReads, queryReads}
@@ -114,7 +111,7 @@ class CortexCtrl(
override def health: HealthStatus.Type = _health
- val router = SimpleRouter {
+ val router: Router = SimpleRouter {
case POST(p"/job") ⇒ createJob
case GET(p"/job/$jobId<[^/]*>") ⇒ getJob(jobId)
case POST(p"/job/_search") ⇒ findJob
@@ -194,33 +191,33 @@ class CortexCtrl(
}
@Timed
- def getAnalyzer(analyzerId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getAnalyzer(analyzerId: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexAnalyzerSrv.getAnalyzer(analyzerId).map { analyzer ⇒
renderer.toOutput(OK, analyzer)
}
}
@Timed
- def getAnalyzerFor(dataType: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getAnalyzerFor(dataType: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexAnalyzerSrv.getAnalyzersFor(dataType).map { analyzers ⇒
renderer.toOutput(OK, analyzers)
}
}
@Timed
- def listAnalyzer: Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def listAnalyzer: Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexAnalyzerSrv.listAnalyzer.map { analyzers ⇒
renderer.toOutput(OK, analyzers)
}
}
- def getResponder(responderId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getResponder(responderId: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexActionSrv.getResponderById(responderId).map { responder ⇒
renderer.toOutput(OK, responder)
}
}
- def getResponders(entityType: String, entityId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getResponders(entityType: String, entityId: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexActionSrv.findResponderFor(entityType, entityId).map { responders ⇒
renderer.toOutput(OK, responders)
}
@@ -265,7 +262,7 @@ class CortexCtrl(
renderer.toOutput(OK, actions, total)
}
- def getAction(actionId: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getAction(actionId: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
cortexActionSrv.getAction(actionId).map { action ⇒
renderer.toOutput(OK, action)
}
diff --git a/thehive-cortex/app/connectors/cortex/controllers/ReportTemplateCtrl.scala b/thehive-cortex/app/connectors/cortex/controllers/ReportTemplateCtrl.scala
index 51ff1bf8d5..8c36a0e14d 100644
--- a/thehive-cortex/app/connectors/cortex/controllers/ReportTemplateCtrl.scala
+++ b/thehive-cortex/app/connectors/cortex/controllers/ReportTemplateCtrl.scala
@@ -1,29 +1,26 @@
package connectors.cortex.controllers
-import javax.inject.{Inject, Singleton}
-
-import scala.collection.JavaConverters._
-import scala.concurrent.{ExecutionContext, Future}
-import scala.io.Source
-import scala.util.control.NonFatal
-
import akka.stream.Materializer
import akka.stream.scaladsl.Sink
+import connectors.cortex.services.ReportTemplateSrv
+import javax.inject.{Inject, Singleton}
+import models.Roles
+import net.lingala.zip4j.ZipFile
+import net.lingala.zip4j.model.FileHeader
+import org.elastic4play.controllers._
+import org.elastic4play.models.JsonFormat.baseModelEntityWrites
+import org.elastic4play.services.JsonFormat.queryReads
+import org.elastic4play.services.{AuxSrv, QueryDSL, QueryDef}
+import org.elastic4play.{BadRequestError, Timed}
import play.api.Logger
import play.api.http.Status
import play.api.libs.json.{JsBoolean, JsFalse, JsObject, JsTrue}
import play.api.mvc._
-import org.elastic4play.{BadRequestError, Timed}
-import org.elastic4play.controllers._
-import org.elastic4play.models.JsonFormat.baseModelEntityWrites
-import org.elastic4play.services.{QueryDSL, QueryDef}
-import org.elastic4play.services.AuxSrv
-import org.elastic4play.services.JsonFormat.queryReads
-import connectors.cortex.services.ReportTemplateSrv
-import models.Roles
-import net.lingala.zip4j.core.ZipFile
-import net.lingala.zip4j.model.FileHeader
+import scala.collection.JavaConverters._
+import scala.concurrent.{ExecutionContext, Future}
+import scala.io.Source
+import scala.util.control.NonFatal
@Singleton
class ReportTemplateCtrl @Inject()(
@@ -48,14 +45,14 @@ class ReportTemplateCtrl @Inject()(
}
@Timed
- def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def get(id: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
reportTemplateSrv
.get(id)
.map(reportTemplate ⇒ renderer.toOutput(OK, reportTemplate))
}
@Timed
- def getContent(analyzerId: String, reportType: String): Action[AnyContent] = authenticated(Roles.read).async { implicit request ⇒
+ def getContent(analyzerId: String, reportType: String): Action[AnyContent] = authenticated(Roles.read).async { _ ⇒
import org.elastic4play.services.QueryDSL._
val (reportTemplates, total) = reportTemplateSrv.find(and("analyzerId" ~= analyzerId, "reportType" ~= reportType), Some("0-1"), Nil)
total.foreach { t ⇒
diff --git a/thehive-cortex/app/connectors/cortex/models/Artifact.scala b/thehive-cortex/app/connectors/cortex/models/Artifact.scala
index 0118a355ed..18495d7e71 100644
--- a/thehive-cortex/app/connectors/cortex/models/Artifact.scala
+++ b/thehive-cortex/app/connectors/cortex/models/Artifact.scala
@@ -6,6 +6,6 @@ import akka.NotUsed
import akka.stream.scaladsl.Source
import akka.util.ByteString
-sealed abstract class CortexArtifact(attributes: JsObject)
-case class FileArtifact(data: Source[ByteString, NotUsed], attributes: JsObject) extends CortexArtifact(attributes)
-case class DataArtifact(data: String, attributes: JsObject) extends CortexArtifact(attributes)
+sealed abstract class CortexArtifact
+case class FileArtifact(data: Source[ByteString, NotUsed], attributes: JsObject) extends CortexArtifact
+case class DataArtifact(data: String, attributes: JsObject) extends CortexArtifact
diff --git a/thehive-cortex/app/connectors/cortex/models/Job.scala b/thehive-cortex/app/connectors/cortex/models/Job.scala
index 9e722df139..85ead46bf5 100644
--- a/thehive-cortex/app/connectors/cortex/models/Job.scala
+++ b/thehive-cortex/app/connectors/cortex/models/Job.scala
@@ -57,7 +57,7 @@ object Job {
}
class Job(model: JobModel, attributes: JsObject) extends EntityDef[JobModel, Job](model, Job.fixJobAttr(attributes)) with JobAttributes {
- override def toJson = super.toJson + ("report" → report().fold[JsValue](JsObject.empty)(r ⇒ Json.parse(r))) // FIXME is parse fails (invalid report)
+ override def toJson: JsObject = super.toJson + ("report" → report().fold[JsValue](JsObject.empty)(r ⇒ Json.parse(r))) // FIXME is parse fails (invalid report)
}
case class CortexJob(
diff --git a/thehive-cortex/app/connectors/cortex/models/JsonFormat.scala b/thehive-cortex/app/connectors/cortex/models/JsonFormat.scala
index 8f7d068f7b..a15f1dae70 100644
--- a/thehive-cortex/app/connectors/cortex/models/JsonFormat.scala
+++ b/thehive-cortex/app/connectors/cortex/models/JsonFormat.scala
@@ -43,24 +43,21 @@ object JsonFormat {
)
private val fileArtifactFormat = OFormat(fileArtifactReads, fileArtifactWrites)
private val dataArtifactFormat = Json.format[DataArtifact]
- private val artifactReads = Reads[CortexArtifact](
+ implicit val artifactReads: Reads[CortexArtifact] = Reads[CortexArtifact](
json ⇒
json.validate[JsObject].flatMap {
case a if a.keys.contains("data") ⇒ json.validate[DataArtifact](dataArtifactFormat)
case _ ⇒ json.validate[FileArtifact](fileArtifactFormat)
}
)
- private val artifactWrites = OWrites[CortexArtifact] {
+ implicit def artifactWrites[A <: CortexArtifact]: OWrites[A] = OWrites[A] {
case dataArtifact: DataArtifact ⇒ dataArtifactFormat.writes(dataArtifact)
case fileArtifact: FileArtifact ⇒ fileArtifactWrites.writes(fileArtifact)
}
- implicit val artifactFormat: OFormat[CortexArtifact] = OFormat(artifactReads, artifactWrites)
+// implicit def artifactFormat[A <: CortexArtifact]: OFormat[A] = OFormat(artifactReads, artifactWrites)
implicit val jobStatusFormat: Format[JobStatus.Type] = enumFormat(JobStatus)
- private def filterObject(json: JsObject, attributes: String*): JsObject =
- JsObject(attributes.flatMap(a ⇒ (json \ a).asOpt[JsValue].map(a → _)))
-
implicit val cortexJobReads: Reads[CortexJob] = Reads[CortexJob](
json ⇒
for {
@@ -68,7 +65,11 @@ object JsonFormat {
analyzerId ← (json \ "workerId").orElse(json \ "analyzerId").validate[String]
analyzerName = (json \ "workerName").orElse(json \ "analyzerName").validate[String].getOrElse(analyzerId)
analyzerDefinition = (json \ "workerDefinitionId").orElse(json \ "analyzerDefinitionId").validate[String].getOrElse(analyzerId)
- attributes = filterObject(json.as[JsObject], "tlp", "message", "parameters")
+ attributes = JsObject(
+ (json \ "tlp").asOpt[JsValue].map("tlp" -> _).toList :::
+ (json \ "message").asOpt[JsValue].map("message" -> _).toList :::
+ (json \ "parameters").asOpt[JsValue].map("parameters" -> _).toList
+ )
artifact = (json \ "artifact")
.validate[CortexArtifact]
.getOrElse {
diff --git a/thehive-cortex/app/connectors/cortex/models/ReportTemplate.scala b/thehive-cortex/app/connectors/cortex/models/ReportTemplate.scala
index 6db3e1dce6..271e885ba2 100644
--- a/thehive-cortex/app/connectors/cortex/models/ReportTemplate.scala
+++ b/thehive-cortex/app/connectors/cortex/models/ReportTemplate.scala
@@ -28,7 +28,7 @@ trait ReportTemplateAttributes { _: AttributeDef ⇒
class ReportTemplateModel @Inject()
extends ModelDef[ReportTemplateModel, ReportTemplate]("reportTemplate", "Report template", "/connector/cortex/reportTemplate")
with ReportTemplateAttributes {
- override def creationHook(parent: Option[BaseEntity], attrs: JsObject) = {
+ override def creationHook(parent: Option[BaseEntity], attrs: JsObject): Future[JsObject] = {
val maybeId = for {
analyzerId ← (attrs \ "analyzerId").asOpt[String]
reportType ← (attrs \ "reportType").asOpt[String]
diff --git a/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala b/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala
index e5961c91c7..451ff4da12 100644
--- a/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala
+++ b/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala
@@ -99,16 +99,16 @@ case class AssignCase(owner: String, status: ActionOperationStatus.Type = Action
}
object ActionOperation {
- val addTagToCaseWrites = Json.writes[AddTagToCase]
- val addTagToArtifactWrites = Json.writes[AddTagToArtifact]
- val createTaskWrites = Json.writes[CreateTask]
- val addCustomFieldsWrites = Json.writes[AddCustomFields]
- val closeTaskWrites = Json.writes[CloseTask]
- val markAlertAsReadWrites = Json.writes[MarkAlertAsRead]
- val addLogToTaskWrites = Json.writes[AddLogToTask]
- val addTagToAlertWrites = Json.writes[AddTagToAlert]
- val addArtifactToCaseWrites = Json.writes[AddArtifactToCase]
- val assignCaseWrites = Json.writes[AssignCase]
+ val addTagToCaseWrites: OWrites[AddTagToCase] = Json.writes[AddTagToCase]
+ val addTagToArtifactWrites: OWrites[AddTagToArtifact] = Json.writes[AddTagToArtifact]
+ val createTaskWrites: OWrites[CreateTask] = Json.writes[CreateTask]
+ val addCustomFieldsWrites: OWrites[AddCustomFields] = Json.writes[AddCustomFields]
+ val closeTaskWrites: OWrites[CloseTask] = Json.writes[CloseTask]
+ val markAlertAsReadWrites: OWrites[MarkAlertAsRead] = Json.writes[MarkAlertAsRead]
+ val addLogToTaskWrites: OWrites[AddLogToTask] = Json.writes[AddLogToTask]
+ val addTagToAlertWrites: OWrites[AddTagToAlert] = Json.writes[AddTagToAlert]
+ val addArtifactToCaseWrites: OWrites[AddArtifactToCase] = Json.writes[AddArtifactToCase]
+ val assignCaseWrites: OWrites[AssignCase] = Json.writes[AssignCase]
implicit val actionOperationReads: Reads[ActionOperation] = Reads[ActionOperation](
json ⇒
(json \ "type").asOpt[String].fold[JsResult[ActionOperation]](JsError("type is missing in action operation")) {
@@ -170,7 +170,7 @@ class ActionOperationSrv @Inject()(
implicit val mat: Materializer
) {
- lazy val logger = Logger(getClass)
+ lazy val logger: Logger = Logger(getClass)
lazy val alertSrv: AlertSrv = alertSrvProvider.get
def findCaseEntity(entity: BaseEntity): Future[Case] = {
@@ -294,7 +294,7 @@ class ActionOperationSrv @Inject()(
case AddArtifactToCase(data, dataType, dataMessage, _, _) ⇒
for {
initialCase ← findCaseEntity(entity)
- artifact ← artifactSrv.create(initialCase.id, Fields.empty.set("data", data).set("dataType", dataType).set("message", dataMessage))
+ _ ← artifactSrv.create(initialCase.id, Fields.empty.set("data", data).set("dataType", dataType).set("message", dataMessage))
} yield operation.updateStatus(ActionOperationStatus.Success, "")
case AssignCase(owner, _, _) ⇒
for {
diff --git a/thehive-cortex/app/connectors/cortex/services/CortexAnalyzerSrv.scala b/thehive-cortex/app/connectors/cortex/services/CortexAnalyzerSrv.scala
index 18cb243c54..65c49cd9e1 100644
--- a/thehive-cortex/app/connectors/cortex/services/CortexAnalyzerSrv.scala
+++ b/thehive-cortex/app/connectors/cortex/services/CortexAnalyzerSrv.scala
@@ -263,7 +263,6 @@ class CortexAnalyzerSrv @Inject()(
cortex
.getAttachment(id)
.flatMap(src ⇒ src.runWith(FileIO.toPath(file)))
- .flatMap(ioResult ⇒ Future.fromTry(ioResult.status))
.flatMap(_ ⇒ attachmentSrv.save(fiv))
.andThen { case _ ⇒ Files.delete(file) }
.map(a ⇒ Some(artifact + ("attachment" → Json.toJson(a))))
diff --git a/thehive-cortex/app/connectors/cortex/services/ReportTemplateSrv.scala b/thehive-cortex/app/connectors/cortex/services/ReportTemplateSrv.scala
index 5748388ddb..766451ece6 100644
--- a/thehive-cortex/app/connectors/cortex/services/ReportTemplateSrv.scala
+++ b/thehive-cortex/app/connectors/cortex/services/ReportTemplateSrv.scala
@@ -1,27 +1,22 @@
package connectors.cortex.services
-import javax.inject.{Inject, Singleton}
-
-import scala.concurrent.{ExecutionContext, Future}
-import scala.util.Try
-
import akka.NotUsed
import akka.stream.scaladsl.Source
+import connectors.cortex.models.{ReportTemplate, ReportTemplateModel}
+import javax.inject.{Inject, Singleton}
+import org.elastic4play.controllers.Fields
+import org.elastic4play.database.ModifyConfig
+import org.elastic4play.services._
import play.api.Logger
import play.api.libs.json.JsObject
-import org.elastic4play.controllers.Fields
-import org.elastic4play.services.{Agg, AuthContext, CreateSrv, DeleteSrv, FindSrv, GetSrv, QueryDef, UpdateSrv}
-import connectors.cortex.models.{ReportTemplate, ReportTemplateModel}
-import services.ArtifactSrv
-
-import org.elastic4play.database.ModifyConfig
+import scala.concurrent.{ExecutionContext, Future}
+import scala.util.Try
@Singleton
class ReportTemplateSrv @Inject()(
reportTemplateModel: ReportTemplateModel,
createSrv: CreateSrv,
- artifactSrv: ArtifactSrv,
getSrv: GetSrv,
updateSrv: UpdateSrv,
deleteSrv: DeleteSrv,
@@ -56,7 +51,4 @@ class ReportTemplateSrv @Inject()(
findSrv[ReportTemplateModel, ReportTemplate](reportTemplateModel, queryDef, range, sortBy)
def stats(queryDef: QueryDef, aggs: Seq[Agg]): Future[JsObject] = findSrv(reportTemplateModel, queryDef, aggs: _*)
-
- def getStats(id: String): Future[JsObject] =
- Future.successful(JsObject.empty)
}
diff --git a/thehive-misp/app/connectors/misp/MispConnector.scala b/thehive-misp/app/connectors/misp/MispConnector.scala
index a3615b8390..466ed199dd 100644
--- a/thehive-misp/app/connectors/misp/MispConnector.scala
+++ b/thehive-misp/app/connectors/misp/MispConnector.scala
@@ -1,14 +1,12 @@
package connectors.misp
+import connectors.ConnectorModule
import javax.inject.Singleton
-
+import play.api.Logger
import play.api.libs.concurrent.AkkaGuiceSupport
-import play.api.{Configuration, Environment, Logger}
-
-import connectors.ConnectorModule
@Singleton
-class MispConnector(environment: Environment, configuration: Configuration) extends ConnectorModule with AkkaGuiceSupport {
+class MispConnector extends ConnectorModule with AkkaGuiceSupport {
private[MispConnector] lazy val logger = Logger(getClass)
override def configure() {
diff --git a/thehive-misp/app/connectors/misp/MispCtrl.scala b/thehive-misp/app/connectors/misp/MispCtrl.scala
index 6e5b2456ee..38ebc65c6c 100644
--- a/thehive-misp/app/connectors/misp/MispCtrl.scala
+++ b/thehive-misp/app/connectors/misp/MispCtrl.scala
@@ -3,20 +3,17 @@ package connectors.misp
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
-
import play.api.http.Status
import play.api.libs.json.{JsObject, Json}
import play.api.mvc._
-import play.api.routing.SimpleRouter
+import play.api.routing.{Router, SimpleRouter}
import play.api.routing.sird.{GET, POST, UrlContext}
import play.api.{Configuration, Logger}
-
import akka.actor.ActorSystem
import connectors.Connector
import javax.inject.{Inject, Singleton}
import models.{HealthStatus, _}
import services.{AlertTransformer, CaseSrv}
-
import org.elastic4play.JsonFormat.tryWrites
import org.elastic4play.controllers.{Authenticated, Renderer}
import org.elastic4play.models.JsonFormat.baseModelEntityWrites
@@ -116,7 +113,7 @@ class MispCtrl(
private[MispCtrl] lazy val logger = Logger(getClass)
- val router = SimpleRouter {
+ val router: Router = SimpleRouter {
case GET(p"/_syncAlerts") ⇒ syncAlerts
case GET(p"/_syncAllAlerts") ⇒ syncAllAlerts
case GET(p"/_syncArtifacts") ⇒ syncArtifacts
diff --git a/thehive-misp/app/connectors/misp/MispExport.scala b/thehive-misp/app/connectors/misp/MispExport.scala
index db7abf38a1..4e7bde9e08 100644
--- a/thehive-misp/app/connectors/misp/MispExport.scala
+++ b/thehive-misp/app/connectors/misp/MispExport.scala
@@ -3,31 +3,27 @@ package connectors.misp
import java.text.SimpleDateFormat
import java.util.Date
-import javax.inject.{Inject, Provider, Singleton}
-import scala.concurrent.{ExecutionContext, Future}
-import scala.util.{Success, Try}
-
-import play.api.Logger
-import play.api.libs.json._
-
+import akka.stream.Materializer
import akka.stream.scaladsl.Sink
-import connectors.misp.JsonFormat.tlpWrites
+import connectors.misp.JsonFormat.{exportedAttributeWrites, tlpWrites}
+import javax.inject.{Inject, Provider, Singleton}
import models.{Artifact, Case}
-import services.{AlertSrv, ArtifactSrv}
-import JsonFormat.exportedAttributeWrites
-import akka.stream.Materializer
-
-import org.elastic4play.{BadRequestError, InternalError}
import org.elastic4play.controllers.Fields
-import org.elastic4play.services.{Attachment, AttachmentSrv, AuthContext}
import org.elastic4play.services.JsonFormat.attachmentFormat
+import org.elastic4play.services.{Attachment, AttachmentSrv, AuthContext}
import org.elastic4play.utils.RichFuture
+import org.elastic4play.{BadRequestError, InternalError}
+import play.api.Logger
+import play.api.libs.json._
+import services.AlertSrv
+
+import scala.concurrent.{ExecutionContext, Future}
+import scala.util.{Success, Try}
@Singleton
class MispExport @Inject()(
mispConfig: MispConfig,
mispSrv: MispSrv,
- artifactSrv: ArtifactSrv,
alertSrvProvider: Provider[AlertSrv],
attachmentSrv: AttachmentSrv,
implicit val ec: ExecutionContext,
@@ -36,7 +32,7 @@ class MispExport @Inject()(
lazy val dateFormat = new SimpleDateFormat("yy-MM-dd")
private[misp] lazy val alertSrv = alertSrvProvider.get
- lazy val logger = Logger(getClass)
+ lazy val logger: Logger = Logger(getClass)
def relatedMispEvent(mispName: String, caseId: String): Future[(Option[String], Option[String])] = {
import org.elastic4play.services.QueryDSL._
@@ -78,7 +74,7 @@ class MispExport @Inject()(
val mispEvent = Json.obj(
"Event" → Json.obj(
"distribution" → 0,
- "threat_level_id" → (4 - severity),
+ "threat_level_id" → math.min(4, math.max(1, 4 - severity)),
"analysis" → 0,
"info" → title,
"date" → dateFormat.format(date),
diff --git a/thehive-misp/app/connectors/misp/MispSrv.scala b/thehive-misp/app/connectors/misp/MispSrv.scala
index d2254fea26..4f176debff 100644
--- a/thehive-misp/app/connectors/misp/MispSrv.scala
+++ b/thehive-misp/app/connectors/misp/MispSrv.scala
@@ -2,28 +2,26 @@ package connectors.misp
import java.util.Date
-import javax.inject.{Inject, Provider, Singleton}
-
-import scala.concurrent.{ExecutionContext, Future}
-import play.api.Logger
-import play.api.libs.json.JsLookupResult.jsLookupResultToJsLookup
-import play.api.libs.json.JsValue.jsValueToJsLookup
-import play.api.libs.json.Json.toJsFieldJsValueWrapper
-import play.api.libs.json._
-import play.api.libs.ws.WSBodyWritables.writeableOf_JsValue
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.{FileIO, Sink, Source}
import connectors.misp.JsonFormat._
+import javax.inject.{Inject, Provider, Singleton}
import models._
-import net.lingala.zip4j.core.ZipFile
+import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.exception.ZipException
-import net.lingala.zip4j.model.FileHeader
-import services._
import org.elastic4play.controllers.{Fields, FileInputValue}
import org.elastic4play.services.{Attachment, AuthContext, TempSrv}
import org.elastic4play.{InternalError, NotFoundError}
+import play.api.Logger
+import play.api.libs.json.JsLookupResult.jsLookupResultToJsLookup
+import play.api.libs.json.JsValue.jsValueToJsLookup
+import play.api.libs.json.Json.toJsFieldJsValueWrapper
+import play.api.libs.json._
+import play.api.libs.ws.WSBodyWritables.writeableOf_JsValue
+import services._
+import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
@Singleton
@@ -64,7 +62,7 @@ class MispSrv @Inject()(
logger.debug(s"Get MISP events from $fromDate")
val date = fromDate.getTime / 1000
Source
- .fromFuture {
+ .future {
mispConnection("events/index")
.post(Json.obj("searchpublish_timestamp" → date))
}
@@ -208,7 +206,7 @@ class MispSrv @Inject()(
case Some(id) ⇒ caseSrv.get(id)
case None ⇒
for {
- caseTemplate ← alertSrv.getCaseTemplate(alert, customCaseTemplate)
+ caseTemplate ← alertSrv.getCaseTemplate(customCaseTemplate)
caze ← caseSrv.create(Fields(alert.toCaseJson), caseTemplate)
_ ← importArtifacts(alert, caze)
} yield caze
@@ -273,10 +271,10 @@ class MispSrv @Inject()(
val zipFile = new ZipFile(file.filepath.toFile)
if (zipFile.isEncrypted)
- zipFile.setPassword("infected")
+ zipFile.setPassword("infected".toCharArray)
// Get the list of file headers from the zip file
- val fileHeaders = zipFile.getFileHeaders.asScala.toList.asInstanceOf[List[FileHeader]]
+ val fileHeaders = zipFile.getFileHeaders.asScala.toList
val (fileNameHeaders, contentFileHeaders) = fileHeaders.partition { fileHeader ⇒
fileHeader.getFileName.endsWith(".filename.txt")
}
@@ -300,7 +298,7 @@ class MispSrv @Inject()(
tempFile = tempSrv.newTemporaryFile("misp", "malware")
_ = logger.info(s"Extract malware file ${file.filepath} in file $tempFile")
- _ = zipFile.extractFile(contentFileHeader, tempFile.getParent.toString, null, tempFile.getFileName.toString)
+ _ = zipFile.extractFile(contentFileHeader, tempFile.getParent.toString, tempFile.getFileName.toString)
} yield FileInputValue(filename, tempFile, "application/octet-stream")).getOrElse(file)
} catch {
case e: ZipException ⇒
@@ -325,9 +323,7 @@ class MispSrv @Inject()(
response
.bodyAsSource
.runWith(FileIO.toPath(tempFile))
- .map { ioResult ⇒
- if (!ioResult.wasSuccessful) // throw an exception if transfer failed
- throw ioResult.getError
+ .map { _ ⇒
val contentType = response.headers.getOrElse("Content-Type", Seq("application/octet-stream")).head
val filename = response
.headers
diff --git a/thehive-misp/app/connectors/misp/MispSynchro.scala b/thehive-misp/app/connectors/misp/MispSynchro.scala
index 95cda14517..f7150a6af2 100644
--- a/thehive-misp/app/connectors/misp/MispSynchro.scala
+++ b/thehive-misp/app/connectors/misp/MispSynchro.scala
@@ -35,15 +35,21 @@ class MispSynchro @Inject()(
tempSrv: TempSrv,
lifecycle: ApplicationLifecycle,
system: ActorSystem,
- implicit val ec: ExecutionContext,
implicit val mat: Materializer
) {
private[misp] lazy val logger = Logger(getClass)
private[misp] lazy val alertSrv = alertSrvProvider.get
+ implicit val ec: ExecutionContext = try {
+ system.dispatchers.lookup("misp-thread-pools")
+ } catch {
+ case e: Throwable =>
+ logger.warn(s"Unable to use MISP specific dispatcher ($e). Fallback to default dispatcher")
+ system.dispatcher
+ }
private[misp] def initScheduler(): Unit = {
- val task = system.scheduler.schedule(0.seconds, mispConfig.interval) {
+ val task = system.scheduler.scheduleWithFixedDelay(0.seconds, mispConfig.interval) {() =>
if (migrationSrv.isReady) {
logger.info("Update of MISP events is starting ...")
userSrv
diff --git a/thehive-misp/conf/reference.conf b/thehive-misp/conf/reference.conf
index 790845e182..f254618bf2 100644
--- a/thehive-misp/conf/reference.conf
+++ b/thehive-misp/conf/reference.conf
@@ -25,3 +25,14 @@ misp {
# Interval between two MISP event import
interval = 1h
}
+
+misp-thread-pool {
+ fork-join-executor {
+ # Min number of threads available for MISP synchronization
+ parallelism-min = 2
+ # Parallelism (threads) ... ceil(available processors * factor)
+ parallelism-factor = 2.0
+ # Max number of threads available for MISP synchronization
+ parallelism-max = 4
+ }
+}
\ No newline at end of file
diff --git a/ui/app/scripts/directives/report-observables.js b/ui/app/scripts/directives/report-observables.js
index 499b5bcc5d..c10f5ff534 100644
--- a/ui/app/scripts/directives/report-observables.js
+++ b/ui/app/scripts/directives/report-observables.js
@@ -67,22 +67,49 @@
return item.selected === true;
}), 'dataType');
- var message = [
- '### Discovered from:',
- '- Observable: **['+ $scope.origin.dataType + '] - ' + $filter('fang')($scope.origin.data) + '**',
- '- Analyzer: **'+ $scope.analyzer + '**'
- ].join('\n');
+
_.each(toImport, function(list, key) {
- var params = {
- dataType: key,
- single: list.length === 1,
- ioc: false,
- sighted: false,
- tlp: 2,
- message: message,
- tags: [{text: 'src:' + $scope.analyzer}]
- };
+ var message = [
+ '### Discovered from:',
+ '- Observable: **['+ $scope.origin.dataType + '] - ' + $filter('fang')($scope.origin.data) + '**',
+ '- Analyzer: **'+ $scope.analyzer + '**'
+ ];
+
+ var params;
+
+ if(list.length === 1) {
+ var obs = list[0];
+
+ if(obs.message) {
+ message.push('- Message: ' + obs.message);
+ }
+
+ params = {
+ dataType: key,
+ single: true,
+ ioc: false,
+ sighted: false,
+ tlp: obs.tlp || 2,
+ message: message.join('\n'),
+ tags: [{text: 'src:' + $scope.analyzer}].concat(_.map(_.uniq(obs.tags), function(i) {
+ return {text: i};
+ }))
+ };
+ } else {
+ params = {
+ dataType: key,
+ single: list.length === 1,
+ ioc: false,
+ sighted: false,
+ tlp: 2,
+ message: message.join('\n'),
+ tags: [{text: 'src:' + $scope.analyzer}]
+ };
+ }
+
+
+
if(key === 'file') {
params.attachment = _.pluck(list, 'attachment');
diff --git a/ui/app/views/directives/report-observables.html b/ui/app/views/directives/report-observables.html
index 8f167f57a6..0f37686f21 100644
--- a/ui/app/views/directives/report-observables.html
+++ b/ui/app/views/directives/report-observables.html
@@ -26,7 +26,8 @@
- |
+ |
+ |
|
Type |
Data |
@@ -38,6 +39,7 @@
offset: (pagination.currentPage-1)*pagination.pageSize |
limitTo: pagination.pageSize ">
|
+ |
|
diff --git a/ui/app/views/login.html b/ui/app/views/login.html
index 18bc6d7729..7b42eede7c 100644
--- a/ui/app/views/login.html
+++ b/ui/app/views/login.html
@@ -22,10 +22,10 @@
-
-
-
-
+
OR
+
+
diff --git a/ui/app/views/partials/admin/case-template/tasks.html b/ui/app/views/partials/admin/case-template/tasks.html
index a09eae453c..e60f16559b 100644
--- a/ui/app/views/partials/admin/case-template/tasks.html
+++ b/ui/app/views/partials/admin/case-template/tasks.html
@@ -7,7 +7,7 @@
- No tasks have been specified.
+ No tasks have been specified.
Add a task
diff --git a/ui/bower.json b/ui/bower.json
index baf64fcae5..4da35f6d5b 100644
--- a/ui/bower.json
+++ b/ui/bower.json
@@ -1,6 +1,6 @@
{
"name": "thehive",
- "version": "3.4.2",
+ "version": "3.5.0-RC1",
"license": "AGPL-3.0",
"dependencies": {
"jquery": "^3.4.1",
diff --git a/ui/package.json b/ui/package.json
index 4203f746c1..43d762078a 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "thehive",
- "version": "3.4.2",
+ "version": "3.5.0-RC1",
"license": "AGPL-3.0",
"repository": {
"type": "git",
diff --git a/version.sbt b/version.sbt
index 06daa17521..95b3e51d97 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "3.4.2-1"
+version in ThisBuild := "3.5.0-RC1-1"