diff --git a/opencti-platform/opencti-front/lang/front/de.json b/opencti-platform/opencti-front/lang/front/de.json index 56617a22d2d2..de5117a994c9 100644 --- a/opencti-platform/opencti-front/lang/front/de.json +++ b/opencti-platform/opencti-front/lang/front/de.json @@ -617,6 +617,7 @@ "description": "beschreibung", "Details": "Einzelheiten", "Detection": "Erkennung", + "Diamond": "Diamant", "Digest with multiple notifiers": "Digest mit mehreren Anmeldern", "Direct relations creations": "Direkte Beziehungen Kreationen", "Direct targeting of this country": "Direktes Ziel dieses Landes", @@ -659,6 +660,7 @@ "Distribution of opinions": "Verteilung der Meinungen", "Distribution of relations": "Verteilung der Beziehungen", "Distribution of relations (including inferred)": "Verteilung der Beziehungen (einschließlich abgeleiteter)", + "Distribution of reports": "Verteilung von Berichten", "Distribution of sources": "Verteilung von Quellen", "Distribution:": "Verteilung:", "Do not display relationships": "Beziehungen nicht anzeigen", @@ -1370,6 +1372,7 @@ "Last 3 months": "Letzte 3 Monate", "Last 6 months": "Letzte 6 Monate", "Last 7 days": "Letzte 7 Tage", + "Last attributions": "Letzte Zuschreibungen", "Last event processed": "Letztes verarbeitetes Ereignis", "Last execution": "Letzte Ausführung", "Last execution traces": "Letzte Ausführungsspuren", @@ -1389,8 +1392,18 @@ "Last run:": "Letzter Lauf:", "Last seen": "Zuletzt gesehen", "last seen": "zuletzt gesehen", + "Last targeted countries": "Letzte Zielländer", + "Last targeted organizations": "Letzte Zielorganisationen", "Last targeted organizations in this sector": "Letzte Zielorganisationen in diesem Sektor", + "Last targeted sectors": "Letzte Zielsektoren", "Last update": "Letzte Aktualisierung", + "Last used attack patterns": "Zuletzt verwendete Angriffsmuster", + "Last used by": "Zuletzt verwendet von", + "Last used domains": "Zuletzt benutzte Domänen", + "Last used infrastructures": "Zuletzt genutzte Infrastrukturen", + "Last used IP addresses": "Zuletzt genutzte IP-Adressen", + "Last used malwares": "Zuletzt verwendete Malwares", + "Last used tools and channels": "Zuletzt genutzte Tools und Kanäle", "Last year": "Letztes Jahr", "Lastname": "Nachname", "Latest containers about the object": "Letzte Container über das Objekt", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "Top Labels (3 letzte Monate)", "Top read or exported entities": "Top gelesene oder exportierte Entitäten", "toSightingId": "Gesehen in/an", + "Total analyses": "Gesamte Analysen", "Total direct relations": "Direkte Beziehungen insgesamt", "Total entities": "Gesamte Entitäten", "Total files in S3": "Gesamtzahl der Dateien in S3", @@ -2745,6 +2759,7 @@ "Victimology (regions)": "Viktimologie (Regionen)", "Victimology (sectors)": "Viktimologie (Sektoren)", "View": "Ansicht", + "View all": "Alle anzeigen", "View all entities created by user": "Alle vom Benutzer erstellten Organisationen anzeigen", "View all relationships created by user": "Alle vom Benutzer erstellten Beziehungen anzeigen", "View the item": "Betrachten Sie das Element", diff --git a/opencti-platform/opencti-front/lang/front/en.json b/opencti-platform/opencti-front/lang/front/en.json index 59c00c178f7b..ee5831f7893d 100644 --- a/opencti-platform/opencti-front/lang/front/en.json +++ b/opencti-platform/opencti-front/lang/front/en.json @@ -617,6 +617,7 @@ "description": "description", "Details": "Details", "Detection": "Detection", + "Diamond": "Diamond", "Digest with multiple notifiers": "Digest with multiple notifiers", "Direct relations creations": "Direct relations creations", "Direct targeting of this country": "Direct targeting of this country", @@ -659,6 +660,7 @@ "Distribution of opinions": "Distribution of opinions", "Distribution of relations": "Distribution of relations", "Distribution of relations (including inferred)": "Distribution of relations (including inferred)", + "Distribution of reports": "Distribution of reports", "Distribution of sources": "Distribution of sources", "Distribution:": "Distribution:", "Do not display relationships": "Do not display relationships", @@ -1370,6 +1372,7 @@ "Last 3 months": "Last 3 months", "Last 6 months": "Last 6 months", "Last 7 days": "Last 7 days", + "Last attributions": "Last attributions", "Last event processed": "Last event processed", "Last execution": "Last execution", "Last execution traces": "Last execution traces", @@ -1389,8 +1392,18 @@ "Last run:": "Last run:", "Last seen": "Last seen", "last seen": "last seen", + "Last targeted countries": "Last targeted countries", + "Last targeted organizations": "Last targeted organizations", "Last targeted organizations in this sector": "Last targeted organizations in this sector", + "Last targeted sectors": "Last targeted sectors", "Last update": "Last update", + "Last used attack patterns": "Last used attack patterns", + "Last used by": "Last used by", + "Last used domains": "Last used domains", + "Last used infrastructures": "Last used infrastructures", + "Last used IP addresses": "Last used IP addresses", + "Last used malwares": "Last used malwares", + "Last used tools and channels": "Last used tools and channels", "Last year": "Last year", "Lastname": "Lastname", "Latest containers about the object": "Latest containers about the object", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "Top Labels (3 last months)", "Top read or exported entities": "Top read or exported entities", "toSightingId": "Sighted in/at", + "Total analyses": "Total analyses", "Total direct relations": "Total direct relations", "Total entities": "Total entities", "Total files in S3": "Total files in S3", @@ -2745,6 +2759,7 @@ "Victimology (regions)": "Victimology (regions)", "Victimology (sectors)": "Victimology (sectors)", "View": "View", + "View all": "View all", "View all entities created by user": "View all entities created by user", "View all relationships created by user": "View all relationships created by user", "View the item": "View the item", diff --git a/opencti-platform/opencti-front/lang/front/es.json b/opencti-platform/opencti-front/lang/front/es.json index b43e9c775090..7f05f039c58c 100644 --- a/opencti-platform/opencti-front/lang/front/es.json +++ b/opencti-platform/opencti-front/lang/front/es.json @@ -617,6 +617,7 @@ "description": "Descripción", "Details": "Detalles", "Detection": "Detección", + "Diamond": "Diamante", "Digest with multiple notifiers": "Compendio con múltiples notificadores", "Direct relations creations": "Creación de relaciones directas", "Direct targeting of this country": "Este paíss como objetivo directo", @@ -659,6 +660,7 @@ "Distribution of opinions": "Distribución de opiniones", "Distribution of relations": "Distribución de relaciones", "Distribution of relations (including inferred)": "Distribución de reñacopmes (incluyendo inferencias)", + "Distribution of reports": "Distribución de informes", "Distribution of sources": "Distribución de fuentes", "Distribution:": "Distribución:", "Do not display relationships": "No mostrar relaciones", @@ -1370,6 +1372,7 @@ "Last 3 months": "Últimos 3 meses", "Last 6 months": "Últimos 6 meses", "Last 7 days": "Últimos 7 días", + "Last attributions": "Últimas atribuciones", "Last event processed": "Último evento procesado", "Last execution": "Última ejecución", "Last execution traces": "Últimas trazas de ejecución", @@ -1389,8 +1392,18 @@ "Last run:": "Última ejecución:", "Last seen": "Última observación", "last seen": "Última observación", + "Last targeted countries": "Últimos países destinatarios", + "Last targeted organizations": "Últimas organizaciones objetivo", "Last targeted organizations in this sector": "Últimas organizaciones atacadas en este sector", + "Last targeted sectors": "Últimos sectores objetivo", "Last update": "Última actualización", + "Last used attack patterns": "Últimos patrones de ataque utilizados", + "Last used by": "Último utilizado por", + "Last used domains": "Últimos dominios utilizados", + "Last used infrastructures": "Últimas infraestructuras utilizadas", + "Last used IP addresses": "Últimas direcciones IP utilizadas", + "Last used malwares": "Últimos malwares utilizados", + "Last used tools and channels": "Últimas herramientas y canales utilizados", "Last year": "Último año", "Lastname": "Apellido", "Latest containers about the object": "Últimos contenedores sobre el objeto", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "Top de etiquetas (tres últimos meses)", "Top read or exported entities": "Entidades más leídas o exportadas", "toSightingId": "Detectado en", + "Total analyses": "Análisis totales", "Total direct relations": "Número de relations directas", "Total entities": "Número de entidades", "Total files in S3": "Total de archivos en S3", @@ -2745,6 +2759,7 @@ "Victimology (regions)": "Victimología (regiones)", "Victimology (sectors)": "Victimología (sectores)", "View": "Vista", + "View all": "Ver todos", "View all entities created by user": "Ver todas las entidades creadas por el usuario", "View all relationships created by user": "Ver todas las relaciones creadas por el usuario", "View the item": "Ver el objeto", diff --git a/opencti-platform/opencti-front/lang/front/fr.json b/opencti-platform/opencti-front/lang/front/fr.json index ceaae65c3af9..4543b3ad6980 100644 --- a/opencti-platform/opencti-front/lang/front/fr.json +++ b/opencti-platform/opencti-front/lang/front/fr.json @@ -617,6 +617,7 @@ "description": "Description", "Details": "Détails", "Detection": "Détection", + "Diamond": "Diamant", "Digest with multiple notifiers": "Digest avec plusieurs notificateurs", "Direct relations creations": "Créations de relations directes", "Direct targeting of this country": "Ciblage direct de ce pays", @@ -659,6 +660,7 @@ "Distribution of opinions": "Distribution des opinions", "Distribution of relations": "Répartition des relations", "Distribution of relations (including inferred)": "Répartition des relations (inférences incluses)", + "Distribution of reports": "Distribution des rapports", "Distribution of sources": "Répartition des sources", "Distribution:": "Répartition:", "Do not display relationships": "Ne pas afficher les relations", @@ -1370,6 +1372,7 @@ "Last 3 months": "3 derniers mois", "Last 6 months": "Derniers 6 mois", "Last 7 days": "Derniers 7 jours", + "Last attributions": "Dernières attributions", "Last event processed": "Dernier événement traité", "Last execution": "Dernière éxecution", "Last execution traces": "Dernières traces d'exécution", @@ -1389,8 +1392,18 @@ "Last run:": "Dernière exécution:", "Last seen": "Dernière observation", "last seen": "Dernière observation", + "Last targeted countries": "Derniers pays visés", + "Last targeted organizations": "Dernières organisations visées", "Last targeted organizations in this sector": "Dernières organisations ciblées dans ce secteur", + "Last targeted sectors": "Derniers secteurs ciblés", "Last update": "Dernière mise à jour", + "Last used attack patterns": "Derniers schémas d'attaque utilisés", + "Last used by": "Dernière utilisation par", + "Last used domains": "Derniers domaines utilisés", + "Last used infrastructures": "Dernières infrastructures utilisées", + "Last used IP addresses": "Dernières adresses IP utilisées", + "Last used malwares": "Derniers malwares utilisés", + "Last used tools and channels": "Derniers outils et canaux utilisés", "Last year": "Dernière année", "Lastname": "Nom", "Latest containers about the object": "Derniers conteneurs à propos de l'object", @@ -2481,7 +2494,7 @@ "This value is overridden for the following entity types": "Cette valeur est remplacée pour les types d'entités suivants", "Threat actor": "Acteur", "Threat actor types": "Type d'acteur", - "Threat actors": "Acteurs de la menace", + "Threat actors": "Acteurs", "Threat actors (group)": "Acteurs (groupe)", "Threat actors (individual)": "Acteurs (individu)", "Threat hunting techniques": "Techniques de recherche de compromission", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "Top Labels (3 derniers mois)", "Top read or exported entities": "Principales entités lues ou exportées", "toSightingId": "Détect dans/en", + "Total analyses": "Analyses totales", "Total direct relations": "Nombre de relations directes", "Total entities": "Nombre d'entités", "Total files in S3": "Nombre total de fichiers sur S3", @@ -2745,6 +2759,7 @@ "Victimology (regions)": "Victimologie (régions)", "Victimology (sectors)": "Victimologie (secteurs)", "View": "Aperçu", + "View all": "Tout voir", "View all entities created by user": "Voir toutes les entités créées par l'utilisateur", "View all relationships created by user": "Afficher toutes les relations créées par l'utilisateur", "View the item": "Voir l'object", diff --git a/opencti-platform/opencti-front/lang/front/ja.json b/opencti-platform/opencti-front/lang/front/ja.json index 450e4867bd13..6caf434a0ecf 100644 --- a/opencti-platform/opencti-front/lang/front/ja.json +++ b/opencti-platform/opencti-front/lang/front/ja.json @@ -617,6 +617,7 @@ "description": "説明", "Details": "詳細", "Detection": "検出", + "Diamond": "ダイヤモンド", "Digest with multiple notifiers": "複数のノーティファイアを持つダイジェスト", "Direct relations creations": "直接的なリレーションの作成", "Direct targeting of this country": "この国家を直接的に標的にしている", @@ -659,6 +660,7 @@ "Distribution of opinions": "オピニオンの分布", "Distribution of relations": "リレーションの分布", "Distribution of relations (including inferred)": "リレーション(推論を含む)の分布", + "Distribution of reports": "レポートの配布\n\n最後の帰属", "Distribution of sources": "ソースの分布", "Distribution:": "分布:", "Do not display relationships": "リレーションシップを表示しない", @@ -1370,6 +1372,7 @@ "Last 3 months": "過去3ヶ月", "Last 6 months": "過去6ヶ月", "Last 7 days": "過去7日間", + "Last attributions": "最終対象国", "Last event processed": "最後に処理されたイベント", "Last execution": "最終実行日時", "Last execution traces": "最後の実行トレース", @@ -1389,8 +1392,18 @@ "Last run:": "最終実行日時: ", "Last seen": "最終観測日時", "last seen": "最終観測日時", + "Last targeted countries": "最後の対象組織", + "Last targeted organizations": "最後に対象となった部門", "Last targeted organizations in this sector": "このセクターで最後に標的となった組織", + "Last targeted sectors": "最後に使用された攻撃パターン", "Last update": "最終更新", + "Last used attack patterns": "最後に使用された", + "Last used by": "最後に使用したドメイン", + "Last used domains": "最後に使用されたインフラ", + "Last used infrastructures": "最後に使用されたIPアドレス", + "Last used IP addresses": "最後に使用されたマルウェア", + "Last used malwares": "最後に使用されたツールとチャンネル", + "Last used tools and channels": "分析合計", "Last year": "過去1年間", "Lastname": "苗字", "Latest containers about the object": "オブジェクトに関する最後のコンテナ", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "上位のラベル(過去3ヶ月)", "Top read or exported entities": "リードまたはエクスポートされたエンティティのトップ", "toSightingId": "目撃情報", + "Total analyses": "すべて表示", "Total direct relations": "直接的なリレーションの総数", "Total entities": "エンティティの総数", "Total files in S3": "S3内の総ファイル数", diff --git a/opencti-platform/opencti-front/lang/front/zh.json b/opencti-platform/opencti-front/lang/front/zh.json index ae868f483d07..19b874d0e4dd 100644 --- a/opencti-platform/opencti-front/lang/front/zh.json +++ b/opencti-platform/opencti-front/lang/front/zh.json @@ -617,6 +617,7 @@ "description": "描述", "Details": "详情", "Detection": "检测", + "Diamond": "钻石", "Digest with multiple notifiers": "具有多个通知器的摘要", "Direct relations creations": "直接关系创建", "Direct targeting of this country": "直接针对该国家/地区", @@ -659,6 +660,7 @@ "Distribution of opinions": "意见分布", "Distribution of relations": "关系分布", "Distribution of relations (including inferred)": "关系分布(包含推理)", + "Distribution of reports": "报告的分发", "Distribution of sources": "源分布", "Distribution:": "分布:", "Do not display relationships": "不显示关系", @@ -1370,6 +1372,7 @@ "Last 3 months": "最近3个月", "Last 6 months": "最近6个月", "Last 7 days": "最近7天", + "Last attributions": "最后归属", "Last event processed": "最后处理的事件", "Last execution": "上次执行时间", "Last execution traces": "最后执行轨迹", @@ -1389,8 +1392,18 @@ "Last run:": "上次运行:", "Last seen": "最后看到", "last seen": "最后看到", + "Last targeted countries": "最后的目标国家", + "Last targeted organizations": "最后的目标组织", "Last targeted organizations in this sector": "在此部门中最近被针对的组织", + "Last targeted sectors": "最后的目标部门", "Last update": "上次更新", + "Last used attack patterns": "最后使用的攻击模式", + "Last used by": "最后使用的", + "Last used domains": "最后使用的领域", + "Last used infrastructures": "最后使用的基础设施", + "Last used IP addresses": "最后使用的 IP 地址", + "Last used malwares": "最后使用的恶意软件", + "Last used tools and channels": "最后使用的工具和渠道", "Last year": "最近1年", "Lastname": "姓氏", "Latest containers about the object": "关于对象的最后一个容器", @@ -2531,6 +2544,7 @@ "Top Labels (3 last months)": "最多标签(最近3个月)", "Top read or exported entities": "读取或导出实体最多的作者", "toSightingId": "目击ID", + "Total analyses": "总分析", "Total direct relations": "直接关系总数", "Total entities": "实体总数", "Total files in S3": "S3 中的文件总数", @@ -2745,6 +2759,7 @@ "Victimology (regions)": "受害者研究(地区))", "Victimology (sectors)": "受害者研究(部门)", "View": "视图", + "View all": "查看全部", "View all entities created by user": "查看用户创建的所有实体", "View all relationships created by user": "查看用户创建的所有关系", "View the item": "查看项目", diff --git a/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBar.jsx b/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBar.jsx new file mode 100644 index 000000000000..2b2225335c67 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBar.jsx @@ -0,0 +1,171 @@ +import React, { Component } from 'react'; +import * as PropTypes from 'prop-types'; +import { compose } from 'ramda'; +import { graphql } from 'react-relay'; +import withTheme from '@mui/styles/withTheme'; +import withStyles from '@mui/styles/withStyles'; +import CircularProgress from '@mui/material/CircularProgress'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import Chart from '../../common/charts/Chart'; +import { QueryRenderer } from '../../../../relay/environment'; +import inject18n from '../../../../components/i18n'; +import { horizontalBarsChartOptions } from '../../../../utils/Charts'; +import { simpleNumberFormat } from '../../../../utils/Number'; +import { getMainRepresentative } from '../../../../utils/defaultRepresentatives'; + +const styles = () => ({ + paper: { + height: 150, + minHeight: 150, + maxHeight: 150, + margin: '10px 0 20px 0', + padding: '0 10px 10px 0', + borderRadius: 4, + }, +}); + +const stixCoreObjectReportsHorizontalBarDistributionQuery = graphql` + query StixCoreObjectReportsHorizontalBarDistributionQuery( + $objectId: String + $field: String! + $operation: StatsOperation! + $limit: Int + ) { + reportsDistribution( + objectId: $objectId + field: $field + operation: $operation + limit: $limit + ) { + label + value + entity { + ... on StixObject { + representative { + main + } + } + } + } + } +`; + +class StixCoreObjectReportsHorizontalBar extends Component { + renderContent() { + const { t, stixCoreObjectId, field, theme } = this.props; + const reportsDistributionVariables = { + objectId: stixCoreObjectId, + field: field || 'report_types', + operation: 'count', + limit: 20, + }; + return ( + { + if ( + props + && props.reportsDistribution + && props.reportsDistribution.length > 0 + ) { + const chartData = props.reportsDistribution.map((n) => ({ + name: getMainRepresentative(n.entity) || n.label, + data: [n.value], + })); + return ( + + ); + } + if (props) { + return ( +
+ + {t('No entities of this type has been found.')} + +
+ ); + } + return ( +
+ + + +
+ ); + }} + /> + ); + } + + render() { + const { t, classes, title, variant, height } = this.props; + return ( +
+ + {title || t('Reports distribution')} + + {variant !== 'inLine' ? ( + + {this.renderContent()} + + ) : ( + this.renderContent() + )} +
+ ); + } +} + +StixCoreObjectReportsHorizontalBar.propTypes = { + stixCoreObjectId: PropTypes.string, + title: PropTypes.string, + field: PropTypes.string, + theme: PropTypes.object, + classes: PropTypes.object, + t: PropTypes.func, +}; + +export default compose( + inject18n, + withTheme, + withStyles(styles), +)(StixCoreObjectReportsHorizontalBar); diff --git a/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBars.jsx b/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBars.jsx index 16f93bd7ccbf..8c3f643f764b 100644 --- a/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBars.jsx +++ b/opencti-platform/opencti-front/src/private/components/analyses/reports/StixCoreObjectReportsHorizontalBars.jsx @@ -26,29 +26,29 @@ const styles = () => ({ }); const stixCoreObjectReportsHorizontalBarsDistributionQuery = graphql` - query StixCoreObjectReportsHorizontalBarsDistributionQuery( - $objectId: String - $field: String! - $operation: StatsOperation! - $limit: Int - ) { - reportsDistribution( - objectId: $objectId - field: $field - operation: $operation - limit: $limit + query StixCoreObjectReportsHorizontalBarsDistributionQuery( + $objectId: String + $field: String! + $operation: StatsOperation! + $limit: Int ) { - label - value - entity { - ... on StixObject { - representative { - main - } + reportsDistribution( + objectId: $objectId + field: $field + operation: $operation + limit: $limit + ) { + label + value + entity { + ... on StixObject { + representative { + main + } + } + } } - } } - } `; class StixCoreObjectReportsHorizontalBars extends Component { @@ -67,8 +67,8 @@ class StixCoreObjectReportsHorizontalBars extends Component { render={({ props }) => { if ( props - && props.reportsDistribution - && props.reportsDistribution.length > 0 + && props.reportsDistribution + && props.reportsDistribution.length > 0 ) { const data = props.reportsDistribution.map((n) => ({ x: getMainRepresentative(n.entity) || n.label, diff --git a/opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelKnowledge.jsx b/opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelKnowledge.jsx index 0d3af6bac1e1..faf27c74deae 100644 --- a/opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelKnowledge.jsx +++ b/opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelKnowledge.jsx @@ -67,6 +67,25 @@ class ChannelKnowledgeComponent extends Component { /> } /> + + } + /> } + /> + } > )} /> + + } + /> )} /> diff --git a/opencti-platform/opencti-front/src/private/components/arsenal/malwares/Root.jsx b/opencti-platform/opencti-front/src/private/components/arsenal/malwares/Root.jsx index 0d303d00f927..90ebd1832f56 100644 --- a/opencti-platform/opencti-front/src/private/components/arsenal/malwares/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/arsenal/malwares/Root.jsx @@ -93,29 +93,6 @@ class RootMalware extends Component { const link = `/dashboard/arsenal/malwares/${malwareId}/knowledge`; return ( <> - - } - > - - - - } - enableQuickSubscription={true} - /> - - - - - - - - - - {isOverview && ( - - )} - + <> - } - /> - } - /> } - /> - } /> - } + +
+ - } + } + enableQuickSubscription={true} /> - + + + + + + + + + {isOverview && ( + )} - /> - -
+ + + } + /> + } + /> + } + /> + + } + /> + } + /> + } + /> + + )} + /> + + + ); } return ; diff --git a/opencti-platform/opencti-front/src/private/components/arsenal/tools/Root.jsx b/opencti-platform/opencti-front/src/private/components/arsenal/tools/Root.jsx index 30a282f3ce51..592411022507 100644 --- a/opencti-platform/opencti-front/src/private/components/arsenal/tools/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/arsenal/tools/Root.jsx @@ -93,21 +93,23 @@ class RootTool extends Component { return ( <> - } + } > diff --git a/opencti-platform/opencti-front/src/private/components/arsenal/tools/ToolKnowledge.jsx b/opencti-platform/opencti-front/src/private/components/arsenal/tools/ToolKnowledge.jsx index 666f16214b11..651b068d85a2 100644 --- a/opencti-platform/opencti-front/src/private/components/arsenal/tools/ToolKnowledge.jsx +++ b/opencti-platform/opencti-front/src/private/components/arsenal/tools/ToolKnowledge.jsx @@ -58,6 +58,25 @@ class ToolKnowledgeComponent extends Component { /> } /> + + } + /> ({ }, item: { height: 38, + fontSize: 9, }, toolbar: theme.mixins.toolbar, })); @@ -34,13 +35,18 @@ const useStyles = makeStyles((theme) => ({ const StixCoreObjectKnowledgeBar = ({ stixCoreObjectLink, availableSections, + stixCoreObjectsDistribution, + attribution = [], }) => { - const { t_i18n } = useFormatter(); + const { t_i18n, n } = useFormatter(); const classes = useStyles(); const location = useLocation(); const { bannerSettings } = useAuth(); - const isInAvailableSection = (sections) => any((filter) => includes(filter, sections), availableSections); + const isInAvailableSection = (sections) => R.any((filter) => R.includes(filter, sections), availableSections); const settingsMessagesBannerHeight = useSettingsMessagesBannerHeight(); + const statistics = stixCoreObjectsDistribution ? R.indexBy(R.prop('label'), stixCoreObjectsDistribution) : {}; + const statisticsVictims = R.sum(R.values(R.pick(['Sector', 'Organization', 'Individual', 'Region', 'Country', 'City'], statistics)).map((o) => o.value)); + const statisticsAttributions = R.sum(R.values(R.pick(attribution, statistics)).map((o) => o.value)); return ( - - + + @@ -78,138 +84,138 @@ const StixCoreObjectKnowledgeBar = ({ } > - {includes('sectors', availableSections) && ( + {R.includes('sectors', availableSections) && ( - - + + )} - {includes('regions', availableSections) && ( + {R.includes('regions', availableSections) && ( - - + + )} - {includes('countries', availableSections) && ( + {R.includes('countries', availableSections) && ( - - + + )} - {includes('areas', availableSections) && ( + {R.includes('areas', availableSections) && ( - - + + )} - {includes('cities', availableSections) && ( + {R.includes('cities', availableSections) && ( - - + + )} - {includes('organizations', availableSections) && ( + {R.includes('organizations', availableSections) && ( - - + + )} - {includes('individuals', availableSections) && ( + {R.includes('individuals', availableSections) && ( - - + + )} - {includes('locations', availableSections) && ( + {R.includes('locations', availableSections) && ( - - + + )} - {includes('used_tools', availableSections) && ( + {R.includes('used_tools', availableSections) && ( - - + + @@ -217,138 +223,138 @@ const StixCoreObjectKnowledgeBar = ({ ) : (
- {includes('sectors', availableSections) && ( + {R.includes('sectors', availableSections) && ( - - + + )} - {includes('regions', availableSections) && ( + {R.includes('regions', availableSections) && ( - - + + )} - {includes('countries', availableSections) && ( + {R.includes('countries', availableSections) && ( - - + + )} - {includes('areas', availableSections) && ( + {R.includes('areas', availableSections) && ( - - + + )} - {includes('cities', availableSections) && ( + {R.includes('cities', availableSections) && ( - - + + )} - {includes('locations', availableSections) && ( + {R.includes('locations', availableSections) && ( - - + + )} - {includes('organizations', availableSections) && ( + {R.includes('organizations', availableSections) && ( - - + + )} - {includes('individuals', availableSections) && ( + {R.includes('individuals', availableSections) && ( - - + + )} - {includes('used_tools', availableSections) && ( + {R.includes('used_tools', availableSections) && ( - - + + @@ -370,96 +376,96 @@ const StixCoreObjectKnowledgeBar = ({ {t_i18n('Threats')} } > - {includes('threats', availableSections) && ( + {R.includes('threats', availableSections) && ( - - + + )} - {includes('attribution', availableSections) && ( + {R.includes('attribution', availableSections) && ( - - + + - + 0 ? ` (${n(statisticsAttributions)})` : ''}`} /> )} - {includes('victimology', availableSections) && ( + {R.includes('victimology', availableSections) && ( - - + + - + 0 ? ` (${n(statisticsVictims)})` : ''}`} /> )} - {includes('threat_actors', availableSections) && ( + {R.includes('threat_actors', availableSections) && ( - - + + )} - {includes('intrusion_sets', availableSections) && ( + {R.includes('intrusion_sets', availableSections) && ( - - + + - + 0 ? ` (${n(statistics['Intrusion-Set'].value)})` : ''}`} /> )} - {includes('campaigns', availableSections) && ( + {R.includes('campaigns', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Campaign.value)})` : ''}`} /> )} @@ -480,76 +486,76 @@ const StixCoreObjectKnowledgeBar = ({ {t_i18n('Arsenal')} } > - {includes('variants', availableSections) && ( + {R.includes('variants', availableSections) && ( - - + + )} - {includes('malwares', availableSections) && ( + {R.includes('malwares', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Malware.value)})` : ''}`} /> )} - {includes('channels', availableSections) && ( + {R.includes('channels', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Channel.value)})` : ''}`} /> )} - {includes('tools', availableSections) && ( + {R.includes('tools', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Tool.value)})` : ''}`} /> )} - {includes('vulnerabilities', availableSections) && ( + {R.includes('vulnerabilities', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Vulnerability.value)})` : ''}`} /> )} @@ -564,36 +570,36 @@ const StixCoreObjectKnowledgeBar = ({ } > - {includes('attack_patterns', availableSections) && ( + {R.includes('attack_patterns', availableSections) && ( - - + + - + 0 ? ` (${n(statistics['Attack-Pattern'].value)})` : ''}`} /> )} - {includes('narratives', availableSections) && ( + {R.includes('narratives', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Narrative.value)})` : ''}`} /> )} @@ -613,52 +619,52 @@ const StixCoreObjectKnowledgeBar = ({ } > - {includes('indicators', availableSections) && ( + {R.includes('indicators', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Indicator.value)})` : ''}`} /> )} - {includes('observables', availableSections) && ( + {R.includes('observables', availableSections) && ( - - + + )} - {includes('infrastructures', availableSections) && ( + {R.includes('infrastructures', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Infrastructure.value)})` : ''}`} /> )} @@ -671,46 +677,46 @@ const StixCoreObjectKnowledgeBar = ({ {t_i18n('Events')} } > - {includes('incidents', availableSections) && ( + {R.includes('incidents', availableSections) && ( - - + + - + 0 ? ` (${n(statistics.Incident.value)})` : ''}`} /> )} - {includes('observed_data', availableSections) && ( + {R.includes('observed_data', availableSections) && ( - - + + - + 0 ? ` (${n(statistics['Observed-Data'].value)})` : ''}`} /> )} - {includes('sightings', availableSections) && ( + {R.includes('sightings', availableSections) && ( - - + + @@ -729,11 +735,11 @@ const StixCoreObjectKnowledgeBar = ({ component={Link} to={`${stixCoreObjectLink}/related`} selected={location.pathname === `${stixCoreObjectLink}/related`} - dense={false} + dense={true} classes={{ root: classes.item }} > - - + + diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectDiamond.jsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectDiamond.jsx new file mode 100644 index 000000000000..d9f1c500d350 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectDiamond.jsx @@ -0,0 +1,1467 @@ +import React, { useCallback } from 'react'; +import { createRefetchContainer, graphql } from 'react-relay'; +import makeStyles from '@mui/styles/makeStyles'; +import ReactFlow, { addEdge, MarkerType, ReactFlowProvider, useEdgesState, useNodesState } from 'reactflow'; +import nodeTypes from './diamond/types/nodes'; +import edgeTypes from './diamond/types/edges'; +import { ErrorBoundary } from '../../Error'; +import { stixDomainObjectThreatDiamondQuery } from './StixDomainObjectThreatDiamondQuery'; + +const useStyles = makeStyles(() => ({ + container: { + margin: 0, + overflow: 'hidden', + }, +})); + +const defaultViewport = { x: 0, y: 0, zoom: 1.5 }; +const proOptions = { account: 'paid-pro', hideAttribution: true }; +const fitViewOptions = { padding: 0.1 }; +const defaultEdgeOptions = { + type: 'straight', + markerEnd: { type: MarkerType.Arrow }, + style: { strokeWidth: 2, strokeDasharray: '3 3' }, +}; + +const StixDomainObjectDiamondComponent = ({ entityLink, data }) => { + const classes = useStyles(); + const { stixDomainObject } = data; + const initialNodes = [ + { + id: 'diamond', + type: 'diamond', + data: { + key: 'diamond', + name: stixDomainObject.name, + }, + position: { x: 100, y: 100 }, + }, + { + id: 'infrastructure', + type: 'infrastructure', + data: { + key: 'infrastructure', + stixDomainObject, + entityLink, + }, + position: { x: 490, y: 60 }, + }, + { + id: 'adversary', + type: 'adversary', + data: { + key: 'adversary', + stixDomainObject, + entityLink, + }, + position: { x: 10, y: -240 }, + }, + { + id: 'victimology', + type: 'victimology', + data: { + key: 'victimology', + stixDomainObject, + entityLink, + }, + position: { x: 10, y: 450 }, + }, + { + id: 'capabilities', + type: 'capabilities', + data: { + key: 'capabilities', + stixDomainObject, + entityLink, + }, + position: { x: -470, y: 60 }, + }, + ]; + const initialEdges = [ + { + id: 'adversary', + type: 'card', + source: 'diamond', + sourceHandle: 'adversary', + target: 'adversary', + data: { + label: 'Adversary', + }, + }, + { + id: 'infrastructure', + type: 'card', + source: 'diamond', + sourceHandle: 'infrastructure', + target: 'infrastructure', + data: { + label: 'Infrastructure', + }, + }, + { + id: 'victimology', + type: 'card', + source: 'diamond', + sourceHandle: 'victimology', + target: 'victimology', + data: { + label: 'Victimology', + }, + }, + { + id: 'capabilities', + type: 'card', + source: 'diamond', + sourceHandle: 'capabilities', + target: 'capabilities', + data: { + label: 'Capabilities', + }, + }, + ]; + const Flow = () => { + const [nodes, , onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + const onConnect = useCallback( + (params) => setEdges((eds) => addEdge(params, eds)), + [setEdges], + ); + return ( + <> + + + ); + }; + return ( + <> + +
+
+ + + +
+
+
+ + ); +}; + +const StixDomainObjectDiamond = createRefetchContainer( + StixDomainObjectDiamondComponent, + { + data: graphql` + fragment StixDomainObjectDiamond_data on Query { + stixDomainObject(id: $id) { + id + entity_type + parent_types + ... on Incident { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + attributedTo: stixCoreRelationships( + relationship_type: "attributed-to" + toTypes: ["Campaign", "Intrusion-Set", "Threat-Actor-Group", "Threat-Actor-Individual"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on ThreatActor { + name + } + ... on IntrusionSet { + name + } + ... on Campaign { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on Campaign { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + attributedTo: stixCoreRelationships( + relationship_type: "attributed-to" + toTypes: ["Intrusion-Set", "Threat-Actor-Group", "Threat-Actor-Individual"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on ThreatActor { + name + } + ... on IntrusionSet { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on IntrusionSet { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + attributedTo: stixCoreRelationships( + relationship_type: "attributed-to" + toTypes: ["Threat-Actor-Group", "Threat-Actor-Individual"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on ThreatActor { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on ThreatActor { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on Malware { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + usedBy: stixCoreRelationships( + relationship_type: "uses" + fromTypes: ["Threat-Actor-Group", "Threat-Actor-Individual", "Intrusion-Set", "Campaign"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ... on ThreatActor { + name + } + ... on IntrusionSet { + name + } + ... on Campaign { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on Tool { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + usedBy: stixCoreRelationships( + relationship_type: "uses" + fromTypes: ["Threat-Actor-Group", "Threat-Actor-Individual", "Intrusion-Set", "Campaign"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ... on ThreatActor { + name + } + ... on IntrusionSet { + name + } + ... on Campaign { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + ... on Channel { + name + aliases + targetedCountries: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Country"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Country { + name + } + } + } + } + } + targetedSectors: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Sector"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Sector { + name + } + } + } + } + } + targetedOrganizations: stixCoreRelationships( + relationship_type: "targets" + toTypes: ["Organization"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Organization { + name + } + } + } + } + } + usedBy: stixCoreRelationships( + relationship_type: "uses" + fromTypes: ["Threat-Actor-Group", "Threat-Actor-Individual", "Intrusion-Set", "Campaign"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ... on ThreatActor { + name + } + ... on IntrusionSet { + name + } + ... on Campaign { + name + } + } + } + } + } + attackPatternsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Attack-Pattern"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on AttackPattern { + name + x_mitre_id + } + } + } + } + } + malwaresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Malware"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Malware { + name + } + } + } + } + } + toolsAndChannelsUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Tool", "Channel"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ... on Tool { + name + } + ... on Channel { + name + } + } + } + } + } + relatedDomains: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["Domain-Name", "Hostname"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + relatedIPs: stixCoreRelationships( + relationship_type: "related-to" + fromTypes: ["IPv4-Addr", "IPv6-Addr"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + from { + ...on StixCyberObservable { + representative { + main + } + } + } + } + } + } + infrastructuresUsed: stixCoreRelationships( + relationship_type: "uses" + toTypes: ["Infrastructure"] + first: 5 + orderBy: created_at + orderMode: desc + ) { + edges { + node { + to { + ...on Infrastructure { + name + } + } + } + } + } + } + } + } + `, + }, + stixDomainObjectThreatDiamondQuery, +); + +export default StixDomainObjectDiamond; diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectHeader.jsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectHeader.jsx index 126c25038ae1..a0a9a3ac214b 100644 --- a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectHeader.jsx +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectHeader.jsx @@ -41,7 +41,6 @@ import { useIsEnforceReference } from '../../../../utils/hooks/useEntitySettings import StixCoreObjectQuickSubscription from '../stix_core_objects/StixCoreObjectQuickSubscription'; import { getMainRepresentative } from '../../../../utils/defaultRepresentatives'; import Transition from '../../../../components/Transition'; -import Loader, { LoaderVariant } from '../../../../components/Loader'; // Deprecated - https://mui.com/system/styles/basics/ // Do not use it for new code. @@ -342,7 +341,7 @@ const StixDomainObjectHeader = (props) => { const triggerData = useLazyLoadQuery(stixCoreObjectQuickSubscriptionContentQuery, { first: 20, ...triggersPaginationOptions }); return ( - }> + }> ((theme) => ({ }, })); -const stixDomainObjectThreatKnowledgeReportsNumberQuery = graphql` - query StixDomainObjectThreatKnowledgeReportsNumberQuery( +const stixDomainObjectThreatKnowledgeContainersNumberQuery = graphql` + query StixDomainObjectThreatKnowledgeContainersNumberQuery( $objectId: String $endDate: DateTime ) { - reportsNumber(objectId: $objectId, endDate: $endDate) { + containersNumber(objectId: $objectId, endDate: $endDate) { total count } @@ -146,7 +148,7 @@ StixDomainObjectThreatKnowledgeProps > = ({ stixDomainObjectId, stixDomainObjectType, displayObservablesStats }) => { const classes = useStyles(); const { n, t_i18n } = useFormatter(); - const [viewType, setViewType] = useState('timeline'); + const [viewType, setViewType] = useState('diamond'); const [timeField, setTimeField] = useState('technical'); const [nestedRelationships, setNestedRelationships] = useState(false); const [openTimeField, setOpenTimeField] = useState(false); @@ -266,7 +268,7 @@ StixDomainObjectThreatKnowledgeProps style={{ height: 120 }} > { - if (props && props.reportsNumber) { - const { total } = props.reportsNumber; - const difference = total - props.reportsNumber.count; + if (props && props.containersNumber) { + const { total } = props.containersNumber; + const difference = total - props.containersNumber.count; return ( -
{t_i18n('Total reports')}
+
{t_i18n('Total analyses')}
{n(total)}
- - - - - - - - + handleChangeViewType(value)} style={{ float: 'left' }} > + -
- - - - - - - {t_i18n('Date reference')} - - - - } - label={t_i18n('Display nested relationships')} + {viewType !== 'diamond' && ( +
+ - -
+ + + + + + {t_i18n('Date reference')} + + + + } + label={t_i18n('Display nested relationships')} + /> + +
+ )}
- - { - if (props) { - if (viewType === 'killchain') { + {viewType !== 'diamond' && ( + + )} + {viewType === 'diamond' ? ( + { + if (props) { + return ( + + ); + } + return ; + }} + /> + ) : ( + { + if (props) { + if (viewType === 'killchain') { + return ( + + ); + } return ( - ); } - return ( - - ); - } - return ; - }} - /> + return ; + }} + /> + )} ); }; diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectsTimeline.jsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectsTimeline.jsx deleted file mode 100644 index e4311e1f8db3..000000000000 --- a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectsTimeline.jsx +++ /dev/null @@ -1,319 +0,0 @@ -import React, { Component } from 'react'; -import * as PropTypes from 'prop-types'; -import { compose } from 'ramda'; -import { graphql } from 'react-relay'; -import withStyles from '@mui/styles/withStyles'; -import Typography from '@mui/material/Typography'; -import Paper from '@mui/material/Paper'; -import Timeline from '@mui/lab/Timeline'; -import TimelineItem from '@mui/lab/TimelineItem'; -import TimelineSeparator from '@mui/lab/TimelineSeparator'; -import TimelineConnector from '@mui/lab/TimelineConnector'; -import TimelineContent from '@mui/lab/TimelineContent'; -import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent'; -import TimelineDot from '@mui/lab/TimelineDot'; -import { Link } from 'react-router-dom'; -import CircularProgress from '@mui/material/CircularProgress'; -import { QueryRenderer } from '../../../../relay/environment'; -import ItemIcon from '../../../../components/ItemIcon'; -import inject18n from '../../../../components/i18n'; -import { getMainRepresentative } from '../../../../utils/defaultRepresentatives'; -import { resolveLink } from '../../../../utils/Entity'; -import { itemColor } from '../../../../utils/Colors'; -import MarkdownDisplay from '../../../../components/MarkdownDisplay'; - -const styles = (theme) => ({ - container: { - width: '100%', - height: '100%', - overflow: 'auto', - }, - paper: { - padding: 15, - }, - itemIcon: { - color: theme.palette.primary.main, - }, - nested: { - paddingLeft: theme.spacing(4), - }, -}); - -const stixDomainObjectsTimelineQuery = graphql` - query StixDomainObjectsTimelineQuery( - $first: Int - $types: [String] - $orderBy: StixDomainObjectsOrdering - $orderMode: OrderingMode - ) { - stixDomainObjects( - first: $first - types: $types - orderBy: $orderBy - orderMode: $orderMode - ) { - edges { - node { - id - entity_type - created - modified - ... on AttackPattern { - name - description - } - ... on Campaign { - name - description - } - ... on CourseOfAction { - name - description - } - ... on Individual { - name - description - } - ... on Organization { - name - description - } - ... on Sector { - name - description - } - ... on System { - name - description - } - ... on Indicator { - name - description - } - ... on Infrastructure { - name - description - } - ... on IntrusionSet { - name - description - } - ... on Position { - name - description - } - ... on City { - name - description - } - ... on AdministrativeArea { - name - description - } - ... on Country { - name - description - } - ... on Region { - name - description - } - ... on Malware { - name - description - } - ... on ThreatActor { - name - description - } - ... on Tool { - name - description - } - ... on Vulnerability { - name - description - } - ... on Incident { - name - description - } - ... on Event { - name - description - } - ... on Channel { - name - description - } - ... on Narrative { - name - description - } - ... on Language { - name - } - ... on DataComponent { - name - } - ... on DataSource { - name - } - ... on Case { - name - } - ... on Note { - attribute_abstract - content - } - ... on Opinion { - opinion - } - } - } - } - } -`; - -class StixDomainObjectsTimeline extends Component { - renderContent() { - const { t, types, fldt, classes } = this.props; - const stixDomainObjectsVariables = { - types, - first: 10, - orderBy: 'created', - orderMode: 'desc', - }; - return ( - { - if ( - props - && props.stixDomainObjects - && props.stixDomainObjects.edges.length > 0 - ) { - const stixDomainObjectsEdges = props.stixDomainObjects.edges; - return ( -
- - {stixDomainObjectsEdges.map((stixDomainObjectEdge) => { - const stixDomainObject = stixDomainObjectEdge.node; - const link = `${resolveLink( - stixDomainObject.entity_type, - )}/${stixDomainObject.id}`; - return ( - - - {fldt(stixDomainObject.created)} - - - - - - - - - - - - - {getMainRepresentative(stixDomainObject)} - -
- -
-
-
-
- ); - })} -
-
- ); - } - if (props) { - return ( -
- - {t('No entities of this type has been found.')} - -
- ); - } - return ( -
- - - -
- ); - }} - /> - ); - } - - render() { - const { t, classes, title, variant } = this.props; - return ( -
- - {title || t('StixDomainObjects timeline')} - - {variant !== 'inLine' ? ( - - {this.renderContent()} - - ) : ( - this.renderContent() - )} -
- ); - } -} - -StixDomainObjectsTimeline.propTypes = { - stixDomainObjectId: PropTypes.string, - data: PropTypes.object, - entityLink: PropTypes.string, - paginationOptions: PropTypes.object, - classes: PropTypes.object, - t: PropTypes.func, -}; - -export default compose( - inject18n, - withStyles(styles), -)(StixDomainObjectsTimeline); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/EdgeCard.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/EdgeCard.tsx new file mode 100644 index 000000000000..cb4943701f7d --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/EdgeCard.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { BaseEdge, EdgeLabelRenderer, EdgeProps, getBezierPath } from 'reactflow'; +import { useTheme } from '@mui/styles'; +import type { Theme } from '../../../../../../../components/Theme'; + +function EdgeLabel({ transform, label }: { transform: string; label: string }) { + const theme = useTheme(); + return ( +
+ {label} +
+ ); +} + +export default function CustomEdge({ + id, sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + style = {}, + markerEnd, + data, +}: EdgeProps) { + const [edgePath, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + return ( + <> + + + + + + ); +} diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/index.ts b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/index.ts new file mode 100644 index 000000000000..9dafab678e04 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/edges/index.ts @@ -0,0 +1,7 @@ +import EdgeCard from './EdgeCard'; + +const edgeTypes = { + card: EdgeCard, +}; + +export default edgeTypes; diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeAdversary.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeAdversary.tsx new file mode 100644 index 000000000000..0026f37fdd63 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeAdversary.tsx @@ -0,0 +1,104 @@ +import React, { memo } from 'react'; +import { Handle, NodeProps, Position } from 'reactflow'; +import { makeStyles } from '@mui/styles'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { Link } from 'react-router-dom'; +import type { Theme } from '../../../../../../../components/Theme'; +import { useFormatter } from '../../../../../../../components/i18n'; +import { emptyFilled } from '../../../../../../../utils/String'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + border: + theme.palette.mode === 'dark' + ? '1px solid rgba(255, 255, 255, 0.12)' + : '1px solid rgba(0, 0, 0, 0.12)', + borderRadius: 4, + backgroundColor: theme.palette.background.paper, + width: 400, + height: 200, + paddingBottom: 25, + }, + nodeContent: { + width: '100%', + height: '100%', + overflowY: 'auto', + padding: 20, + }, + handle: { + visibility: 'hidden', + }, + label: { + marginTop: 20, + }, + buttonExpand: { + position: 'absolute', + left: 0, + bottom: 0, + width: '100%', + height: 25, + color: theme.palette.primary.main, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .1)' + : 'rgba(0, 0, 0, .1)', + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .2)' + : 'rgba(0, 0, 0, .2)', + }, + }, +})); + +const NodeAdversary = ({ data }: NodeProps) => { + const classes = useStyles(); + const { stixDomainObject, entityLink } = data; + const isArsenal = ['Malware', 'Tool', 'Channel'].includes(stixDomainObject.entity_type); + const aliases = stixDomainObject.aliases.slice(0, 5).join(', '); + const attributedTo = (stixDomainObject.attributedTo?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const usedBy = (stixDomainObject.usedBy?.edges ?? []) + .map((n: { node: { from: { name: string } } }) => n?.node?.from?.name) + .join(', '); + const { t_i18n } = useFormatter(); + return ( +
+
+ + {t_i18n('Aliases')} + + {emptyFilled(aliases)} + + {isArsenal ? t_i18n('Last used by') : t_i18n('Last attributions')} + + {isArsenal ? emptyFilled(usedBy) : emptyFilled(attributedTo)} +
+ + +
+ ); +}; + +export default memo(NodeAdversary); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCapabilities.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCapabilities.tsx new file mode 100644 index 000000000000..c35545178baf --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCapabilities.tsx @@ -0,0 +1,109 @@ +import React, { memo } from 'react'; +import { Handle, NodeProps, Position } from 'reactflow'; +import { makeStyles } from '@mui/styles'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { Link } from 'react-router-dom'; +import type { Theme } from '../../../../../../../components/Theme'; +import { useFormatter } from '../../../../../../../components/i18n'; +import { emptyFilled } from '../../../../../../../utils/String'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + border: + theme.palette.mode === 'dark' + ? '1px solid rgba(255, 255, 255, 0.12)' + : '1px solid rgba(0, 0, 0, 0.12)', + borderRadius: 4, + backgroundColor: theme.palette.background.paper, + width: 400, + height: 300, + paddingBottom: 25, + }, + nodeContent: { + width: '100%', + height: '100%', + overflowY: 'auto', + padding: 20, + }, + handle: { + visibility: 'hidden', + }, + label: { + marginTop: 20, + }, + buttonExpand: { + position: 'absolute', + left: 0, + bottom: 0, + width: '100%', + height: 25, + color: theme.palette.primary.main, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .1)' + : 'rgba(0, 0, 0, .1)', + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .2)' + : 'rgba(0, 0, 0, .2)', + }, + }, +})); + +const NodeCapabilities = ({ data }: NodeProps) => { + const classes = useStyles(); + const { stixDomainObject, entityLink } = data; + const usedAttackPatterns = (stixDomainObject.attackPatternsUsed?.edges ?? []) + .map((n: { node: { to: { name: string, x_mitre_id: string } } }) => (n?.node?.to?.x_mitre_id ? `[${n?.node?.to?.x_mitre_id}] ${n?.node?.to?.name}` : n?.node?.to?.name)) + .join(', '); + const usedMalwares = (stixDomainObject.malwaresUsed?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const usedToolsAndChannels = (stixDomainObject.toolsAndChannelsUsed?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const { t_i18n } = useFormatter(); + return ( +
+
+ + {t_i18n('Last used attack patterns')} + + {emptyFilled(usedAttackPatterns)} + + {t_i18n('Last used malwares')} + + {emptyFilled(usedMalwares)} + + {t_i18n('Last used tools and channels')} + + {emptyFilled(usedToolsAndChannels)} +
+ + +
+ ); +}; + +export default memo(NodeCapabilities); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCard.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCard.tsx new file mode 100644 index 000000000000..39078808a159 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeCard.tsx @@ -0,0 +1,60 @@ +import React, { memo } from 'react'; +import { Handle, NodeProps, Position } from 'reactflow'; +import { makeStyles } from '@mui/styles'; +import Typography from '@mui/material/Typography'; +import type { Theme } from '../../../../../../../components/Theme'; +import { useFormatter } from '../../../../../../../components/i18n'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + border: + theme.palette.mode === 'dark' + ? '1px solid rgba(255, 255, 255, 0.12)' + : '1px solid rgba(0, 0, 0, 0.12)', + borderRadius: 4, + backgroundColor: theme.palette.background.paper, + width: 400, + height: 400, + padding: 20, + }, + handle: { + visibility: 'hidden', + }, +})); + +const NodeCard = ({ data }: NodeProps) => { + const classes = useStyles(); + const { t_i18n } = useFormatter(); + let position; + switch (data.key) { + case 'capabilities': + position = Position.Right; + break; + case 'victimology': + position = Position.Top; + break; + case 'adversary': + position = Position.Bottom; + break; + default: + position = Position.Left; + } + return ( +
+ + {t_i18n('Type')} + + +
+ ); +}; + +export default memo(NodeCard); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeDiamond.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeDiamond.tsx new file mode 100644 index 000000000000..e6ae32c76466 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeDiamond.tsx @@ -0,0 +1,121 @@ +import React, { memo } from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; +import makeStyles from '@mui/styles/makeStyles'; +import type { Theme } from '../../../../../../../components/Theme'; +import ItemIcon from '../../../../../../../components/ItemIcon'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + width: 200, + height: 200, + lineHeight: '200px', + textAlign: 'center', + margin: 10, + '&::before': { + position: 'absolute', + content: '""', + top: 0, + left: 0, + height: '100%', + width: '100%', + transform: 'rotate(45deg)', + boxShadow: '0px 0px 12px gray', + }, + '&::after': { + position: 'absolute', + content: '""', + top: 10, + left: 10, + height: 'calc(100% - 22px)', /* -22px is 2 * 10px gap on either side - 2px border on either side */ + width: 'calc(100% - 22px)', /* -22px is 2 * 10px gap on either side - 2px border on either side */ + border: `1px solid ${theme.palette.primary.main}`, + transform: 'rotate(45deg)', + }, + }, + handleTop: { + top: -50, + }, + handleRight: { + right: -50, + }, + handleBottom: { + bottom: -50, + }, + handleLeft: { + left: -50, + }, + adversary: { + position: 'absolute', + top: -85, + left: 81, + }, + infrastructure: { + position: 'absolute', + top: 10, + right: -5, + }, + victimology: { + position: 'absolute', + top: 110, + left: 81, + }, + capabilities: { + position: 'absolute', + top: 10, + left: -5, + }, +})); + +const NodeDiamond = ({ data }: NodeProps) => { + const classes = useStyles(); + return ( +
+ {data.name} +
+ +
+
+ +
+
+ +
+
+ +
+ + + + +
+ ); +}; + +export default memo(NodeDiamond); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeInfrastructure.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeInfrastructure.tsx new file mode 100644 index 000000000000..0b3ba07b99dc --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeInfrastructure.tsx @@ -0,0 +1,109 @@ +import React, { memo } from 'react'; +import { Handle, NodeProps, Position } from 'reactflow'; +import { makeStyles } from '@mui/styles'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { Link } from 'react-router-dom'; +import type { Theme } from '../../../../../../../components/Theme'; +import { useFormatter } from '../../../../../../../components/i18n'; +import { emptyFilled } from '../../../../../../../utils/String'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + border: + theme.palette.mode === 'dark' + ? '1px solid rgba(255, 255, 255, 0.12)' + : '1px solid rgba(0, 0, 0, 0.12)', + borderRadius: 4, + backgroundColor: theme.palette.background.paper, + width: 400, + height: 300, + paddingBottom: 25, + }, + nodeContent: { + width: '100%', + height: '100%', + overflowY: 'auto', + padding: 20, + }, + handle: { + visibility: 'hidden', + }, + label: { + marginTop: 20, + }, + buttonExpand: { + position: 'absolute', + left: 0, + bottom: 0, + width: '100%', + height: 25, + color: theme.palette.primary.main, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .1)' + : 'rgba(0, 0, 0, .1)', + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .2)' + : 'rgba(0, 0, 0, .2)', + }, + }, +})); + +const NodeInfrastructure = ({ data }: NodeProps) => { + const classes = useStyles(); + const { stixDomainObject, entityLink } = data; + const usedIPs = (stixDomainObject.relatedIPs?.edges ?? []) + .map((n: { node: { from: { representative: { main: string } } } }) => n?.node?.from?.representative?.main) + .join(', '); + const usedDomains = (stixDomainObject.relatedDomains?.edges ?? []) + .map((n: { node: { from: { representative: { main: string } } } }) => n?.node?.from?.representative?.main) + .join(', '); + const usedInfrastructures = (stixDomainObject.infrastructuresUsed?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const { t_i18n } = useFormatter(); + return ( +
+
+ + {t_i18n('Last used IP addresses')} + + {emptyFilled(usedIPs)} + + {t_i18n('Last used domains')} + + {emptyFilled(usedDomains)} + + {t_i18n('Last used infrastructures')} + + {emptyFilled(usedInfrastructures)} +
+ + +
+ ); +}; + +export default memo(NodeInfrastructure); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeVictimology.tsx b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeVictimology.tsx new file mode 100644 index 000000000000..4a05034f5962 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/NodeVictimology.tsx @@ -0,0 +1,109 @@ +import React, { memo } from 'react'; +import { Handle, NodeProps, Position } from 'reactflow'; +import { makeStyles } from '@mui/styles'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { Link } from 'react-router-dom'; +import type { Theme } from '../../../../../../../components/Theme'; +import { useFormatter } from '../../../../../../../components/i18n'; +import { emptyFilled } from '../../../../../../../utils/String'; + +// Deprecated - https://mui.com/system/styles/basics/ +// Do not use it for new code. +const useStyles = makeStyles((theme) => ({ + node: { + position: 'relative', + border: + theme.palette.mode === 'dark' + ? '1px solid rgba(255, 255, 255, 0.12)' + : '1px solid rgba(0, 0, 0, 0.12)', + borderRadius: 4, + backgroundColor: theme.palette.background.paper, + width: 400, + height: 300, + paddingBottom: 25, + }, + nodeContent: { + width: '100%', + height: '100%', + overflowY: 'auto', + padding: 20, + }, + handle: { + visibility: 'hidden', + }, + label: { + marginTop: 20, + }, + buttonExpand: { + position: 'absolute', + left: 0, + bottom: 0, + width: '100%', + height: 25, + color: theme.palette.primary.main, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .1)' + : 'rgba(0, 0, 0, .1)', + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .2)' + : 'rgba(0, 0, 0, .2)', + }, + }, +})); + +const NodeVictimology = ({ data }: NodeProps) => { + const classes = useStyles(); + const { stixDomainObject, entityLink } = data; + const targetedCountries = (stixDomainObject.targetedCountries?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const targetedSectors = (stixDomainObject.targetedSectors?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const targetedOrganizations = (stixDomainObject.targetedOrganizations?.edges ?? []) + .map((n: { node: { to: { name: string } } }) => n?.node?.to?.name) + .join(', '); + const { t_i18n } = useFormatter(); + return ( +
+
+ + {t_i18n('Last targeted countries')} + + {emptyFilled(targetedCountries)} + + {t_i18n('Last targeted sectors')} + + {emptyFilled(targetedSectors)} + + {t_i18n('Last targeted organizations')} + + {emptyFilled(targetedOrganizations)} +
+ + +
+ ); +}; + +export default memo(NodeVictimology); diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/index.ts b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/index.ts new file mode 100644 index 000000000000..6fcd2d5a0826 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/diamond/types/nodes/index.ts @@ -0,0 +1,19 @@ +import { NodeTypes } from 'reactflow'; + +import NodeDiamond from './NodeDiamond'; +import NodeCard from './NodeCard'; +import NodeAdversary from './NodeAdversary'; +import NodeVictimology from './NodeVictimology'; +import NodeInfrastructure from './NodeInfrastructure'; +import NodeCapabilities from './NodeCapabilities'; + +const nodeTypes: NodeTypes = { + diamond: NodeDiamond, + card: NodeCard, + adversary: NodeAdversary, + victimology: NodeVictimology, + infrastructure: NodeInfrastructure, + capabilities: NodeCapabilities, +}; + +export default nodeTypes; diff --git a/opencti-platform/opencti-front/src/private/components/events/incidents/Root.tsx b/opencti-platform/opencti-front/src/private/components/events/incidents/Root.tsx index b0db879bb0c6..1809be85b2f8 100644 --- a/opencti-platform/opencti-front/src/private/components/events/incidents/Root.tsx +++ b/opencti-platform/opencti-front/src/private/components/events/incidents/Root.tsx @@ -51,6 +51,10 @@ const incidentQuery = graphql` name aliases x_opencti_graph_data + stixCoreObjectsDistribution(field: "entity_type", operation: count) { + label + value + } ...Incident_incident ...IncidentKnowledge_incident ...StixCoreObjectContent_stixCoreObject @@ -82,6 +86,7 @@ const RootIncidentComponent = ({ queryRef }) => { useSubscription(subConfig); const data = usePreloadedQuery(incidentQuery, queryRef); const { incident, connectorsForImport, connectorsForExport } = data; + const link = `/dashboard/events/incidents/${incidentId}/knowledge`; const isOverview = location.pathname === `/dashboard/events/incidents/${incident?.id}`; const paddingRightValue = () => { if (location.pathname.includes(`/dashboard/events/incidents/${incident.id}/knowledge`)) return 200; @@ -92,123 +97,147 @@ const RootIncidentComponent = ({ queryRef }) => { return ( <> {incident ? ( -
- - - - - - - - - - - - {isOverview && ( - - )} - + <> - } - /> - - )} - /> } - /> - } /> - - )} + +
+ - - )} + - + + + + + + + + + {isOverview && ( + )} - /> - -
+ + + } + /> + + )} + /> + } + /> + + } + /> + + )} + /> + + )} + /> + + )} + /> + +
+ ) : ( )} @@ -221,36 +250,14 @@ const RootIncident = () => { const queryRef = useQueryLoading(incidentQuery, { id: incidentId, }); - const link = `/dashboard/events/incidents/${incidentId}/knowledge`; return ( -
- - - } - /> - + <> {queryRef && ( }> )} -
+ ); }; export default RootIncident; diff --git a/opencti-platform/opencti-front/src/private/components/techniques/narratives/NarrativeKnowledge.jsx b/opencti-platform/opencti-front/src/private/components/techniques/narratives/NarrativeKnowledge.jsx index f8378683f99a..11e08872caa2 100644 --- a/opencti-platform/opencti-front/src/private/components/techniques/narratives/NarrativeKnowledge.jsx +++ b/opencti-platform/opencti-front/src/private/components/techniques/narratives/NarrativeKnowledge.jsx @@ -2,11 +2,11 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; import { Route, Routes } from 'react-router-dom'; import { createFragmentContainer, graphql } from 'react-relay'; +import StixDomainObjectKnowledge from '../../common/stix_domain_objects/StixDomainObjectKnowledge'; import withRouter from '../../../../utils/compat-router/withRouter'; import EntityStixCoreRelationships from '../../common/stix_core_relationships/EntityStixCoreRelationships'; import StixCoreRelationship from '../../common/stix_core_relationships/StixCoreRelationship'; import EntityStixSightingRelationships from '../../events/stix_sighting_relationships/EntityStixSightingRelationships'; -import StixDomainObjectThreatKnowledge from '../../common/stix_domain_objects/StixDomainObjectThreatKnowledge'; import StixSightingRelationship from '../../events/stix_sighting_relationships/StixSightingRelationship'; import EntityStixCoreRelationshipsStixCyberObservable from '../../common/stix_core_relationships/views/stix_cyber_observable/EntityStixCoreRelationshipsStixCyberObservable'; @@ -38,7 +38,7 @@ class NarrativeKnowledgeComponent extends Component { diff --git a/opencti-platform/opencti-front/src/private/components/threats/campaigns/Root.jsx b/opencti-platform/opencti-front/src/private/components/threats/campaigns/Root.jsx index c8600e31e620..494ca5b7c999 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/campaigns/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/threats/campaigns/Root.jsx @@ -48,6 +48,10 @@ const campaignQuery = graphql` name aliases x_opencti_graph_data + stixCoreObjectsDistribution(field: "entity_type", operation: count) { + label + value + } ...Campaign_campaign ...CampaignKnowledge_campaign ...FileImportViewer_entity @@ -90,32 +94,7 @@ class RootCampaign extends Component { const link = `/dashboard/threats/campaigns/${campaignId}/knowledge`; return ( -
- - - } - /> - + <> - - } - enableQuickSubscription={true} - /> - - - - - - - - - - {isOverview && ( - - )} - + <> - } + + } /> - - } + +
+ - - } + } + enableQuickSubscription={true} /> - + + + + + + + + + {isOverview && ( + + )} + + + } - /> - + /> + } - /> - + /> + } - /> - + /> + } - /> - -
+ /> + + } + /> + + } + /> + + } + /> + +
+ ); } return ; @@ -249,7 +257,7 @@ class RootCampaign extends Component { return ; }} /> -
+ ); } } diff --git a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/Root.jsx b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/Root.jsx index 9adb6a8c26fa..655f5f2afed4 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/Root.jsx @@ -50,8 +50,12 @@ const intrusionSetQuery = graphql` aliases objectMarking { id - } + } x_opencti_graph_data + stixCoreObjectsDistribution(field: "entity_type", operation: count) { + label + value + } ...IntrusionSet_intrusionSet ...IntrusionSetKnowledge_intrusionSet ...FileImportViewer_entity @@ -92,36 +96,9 @@ class RootIntrusionSet extends Component { location, params: { intrusionSetId }, } = this.props; - const link = `/dashboard/threats/intrusion_sets/${intrusionSetId}/knowledge`; return ( <> - - - } - /> - - - } - enableQuickSubscription={true} - enableAskAi={true} - /> - - - - - - - - - - {isOverview && ( - - )} - + <> + } /> - - } + +
+ - - -
- } + } + enableQuickSubscription={true} + enableAskAi={true} /> - + + + + + + + + + {isOverview && ( + + )} + + + } - /> - + /> + } - /> - + /> + + +
} - /> - + /> + } - /> - - + /> + + } + /> + + } + /> + + } + /> + + + ); } return ; diff --git a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/Root.jsx b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/Root.jsx index d297f9bcb952..b1f44c9ec077 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/Root.jsx @@ -49,6 +49,10 @@ const ThreatActorGroupQuery = graphql` name aliases x_opencti_graph_data + stixCoreObjectsDistribution(field: "entity_type", operation: count) { + label + value + } ...ThreatActorGroup_ThreatActorGroup ...ThreatActorGroupKnowledge_ThreatActorGroup ...FileImportViewer_entity @@ -92,33 +96,6 @@ class RootThreatActorGroup extends Component { const link = `/dashboard/threats/threat_actors_group/${threatActorGroupId}/knowledge`; return ( <> - - - } - /> - - - } - enableQuickSubscription={true} - /> - - - - - - - - - - {isOverview && ( - - )} - + <> + } /> - - } + +
+ - - } + } + enableQuickSubscription={true} /> - + + + + + + + + + {isOverview && ( + + )} + + + } - /> - + /> + } - /> - + /> + } - /> - + /> + } - /> - -
+ /> + + } + /> + + } + /> + + } + /> + + + ); } return ; diff --git a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_individual/Root.tsx b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_individual/Root.tsx index 2702b3a25ecf..e25d258dac90 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_individual/Root.tsx +++ b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_individual/Root.tsx @@ -53,6 +53,10 @@ const ThreatActorIndividualQuery = graphql` name aliases x_opencti_graph_data + stixCoreObjectsDistribution(field: "entity_type", operation: count) { + label + value + } ...ThreatActorIndividual_ThreatActorIndividual ...ThreatActorIndividualKnowledge_ThreatActorIndividual ...FileImportViewer_entity @@ -100,37 +104,38 @@ const RootThreatActorIndividualComponent = ({ const paddingRight = getPaddingRight(location.pathname, data.id, '/dashboard/threats/threat_actors_individual'); return ( <> - - + + + } /> - } - /> - - <> - {data ? ( +
{isOverview && ( - + )} @@ -243,10 +248,10 @@ const RootThreatActorIndividualComponent = ({ />
- ) : ( - - )} - + + ) : ( + + )} ); }; diff --git a/opencti-platform/opencti-front/src/schema/relay.schema.graphql b/opencti-platform/opencti-front/src/schema/relay.schema.graphql index 00898d4bf634..2d7d28ce8ac9 100644 --- a/opencti-platform/opencti-front/src/schema/relay.schema.graphql +++ b/opencti-platform/opencti-front/src/schema/relay.schema.graphql @@ -7444,6 +7444,7 @@ type Query { container(id: String): Container containers(first: Int, after: ID, orderBy: ContainersOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String, toStix: Boolean): ContainerConnection containersObjectsOfObject(id: String!, types: [String], filters: FilterGroup, search: String): StixObjectOrStixRelationshipConnection + containersNumber(objectId: String, endDate: DateTime): Number note(id: String): Note notes(first: Int, after: ID, orderBy: NotesOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String, toStix: Boolean): NoteConnection notesNumber(objectId: String, endDate: DateTime): Number diff --git a/opencti-platform/opencti-front/src/utils/Charts.js b/opencti-platform/opencti-front/src/utils/Charts.js index 84e97a2b933b..3ea5706ca62d 100644 --- a/opencti-platform/opencti-front/src/utils/Charts.js +++ b/opencti-platform/opencti-front/src/utils/Charts.js @@ -369,6 +369,7 @@ export const verticalBarsChartOptions = ( * @param {boolean} total * @param {string[]} categories * @param {boolean} legend + * @param {string} stackType */ export const horizontalBarsChartOptions = ( theme, @@ -382,6 +383,7 @@ export const horizontalBarsChartOptions = ( total = false, categories = null, legend = false, + stackType = 'normal', ) => ({ events: ['xAxisLabelClick'], chart: { @@ -390,6 +392,7 @@ export const horizontalBarsChartOptions = ( toolbar: toolbarOptions, foreColor: theme.palette.text.secondary, stacked, + stackType, width: '100%', height: '100%', events: { @@ -471,7 +474,7 @@ export const horizontalBarsChartOptions = ( mode: theme.palette.mode, }, dataLabels: { - enabled: false, + enabled: stackType === '100%', }, colors: [ theme.palette.primary.main, @@ -486,6 +489,7 @@ export const horizontalBarsChartOptions = ( }, }, grid: { + show: stackType !== '100%', borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, .1)' @@ -504,6 +508,7 @@ export const horizontalBarsChartOptions = ( xaxis: { categories: categories ?? [], labels: { + show: stackType !== '100%', formatter: (value) => (xFormatter ? xFormatter(value) : value), style: { fontFamily: '"IBM Plex Sans", sans-serif', @@ -515,7 +520,9 @@ export const horizontalBarsChartOptions = ( tickAmount: adjustTicks ? 1 : undefined, }, yaxis: { + show: stackType !== '100%', labels: { + show: stackType !== '100%', formatter: (value) => (yFormatter && typeof value === 'number' ? yFormatter(value) : value), style: { fontFamily: '"IBM Plex Sans", sans-serif', diff --git a/opencti-platform/opencti-front/src/utils/graph/EntitiesDetailsRightBar.tsx b/opencti-platform/opencti-front/src/utils/graph/EntitiesDetailsRightBar.tsx index 3d8a11586d47..b2531663373a 100644 --- a/opencti-platform/opencti-front/src/utils/graph/EntitiesDetailsRightBar.tsx +++ b/opencti-platform/opencti-front/src/utils/graph/EntitiesDetailsRightBar.tsx @@ -32,6 +32,7 @@ const useStyles = makeStyles(() => ({ maxHeight: '60%', padding: '60px 0 20px 20px', zIndex: 900, + borderRadius: 4, }, external: { marginTop: -2, diff --git a/opencti-platform/opencti-graphql/config/schema/opencti.graphql b/opencti-platform/opencti-graphql/config/schema/opencti.graphql index 82328e8f5982..11e7ad277202 100644 --- a/opencti-platform/opencti-graphql/config/schema/opencti.graphql +++ b/opencti-platform/opencti-graphql/config/schema/opencti.graphql @@ -11575,6 +11575,7 @@ type Query { filters: FilterGroup search: String ): StixObjectOrStixRelationshipConnection @auth(for: [KNOWLEDGE]) + containersNumber(objectId: String, endDate: DateTime): Number @auth(for: [KNOWLEDGE, EXPLORE]) note(id: String): Note @auth(for: [KNOWLEDGE]) notes( first: Int diff --git a/opencti-platform/opencti-graphql/src/domain/container.js b/opencti-platform/opencti-graphql/src/domain/container.js index 529b34766f93..ca01ca7a3964 100644 --- a/opencti-platform/opencti-graphql/src/domain/container.js +++ b/opencti-platform/opencti-graphql/src/domain/container.js @@ -1,7 +1,7 @@ import * as R from 'ramda'; import { v4 as uuidv4 } from 'uuid'; -import { RELATION_OBJECT } from '../schema/stixRefRelationship'; -import { listAllThings } from '../database/middleware'; +import { RELATION_CREATED_BY, RELATION_OBJECT } from '../schema/stixRefRelationship'; +import { listAllThings, timeSeriesEntities } from '../database/middleware'; import { internalFindByIds, internalLoadById, listEntities, listEntitiesThroughRelationsPaginated, storeLoadById } from '../database/middleware-loader'; import { ABSTRACT_BASIC_RELATIONSHIP, ABSTRACT_STIX_REF_RELATIONSHIP, ABSTRACT_STIX_RELATIONSHIP, buildRefRelationKey, ENTITY_TYPE_CONTAINER } from '../schema/general'; import { isStixDomainObjectContainer } from '../schema/stixDomainObject'; @@ -72,6 +72,68 @@ export const objects = async (context, user, containerId, args) => { return listEntitiesThroughRelationsPaginated(context, user, containerId, RELATION_OBJECT, types, false, false, baseOpts); }; +export const containersNumber = (context, user, args) => { + return { + count: elCount(context, user, READ_INDEX_STIX_DOMAIN_OBJECTS, { ...args, types: [ENTITY_TYPE_CONTAINER] }), + total: elCount( + context, + user, + READ_INDEX_STIX_DOMAIN_OBJECTS, + { ...R.dissoc('endDate', args), types: [ENTITY_TYPE_CONTAINER] } + ), + }; +}; + +export const containersTimeSeriesByEntity = (context, user, args) => { + const { objectId } = args; + const filters = addFilter(args.filters, buildRefRelationKey(RELATION_OBJECT, '*'), objectId); + return timeSeriesEntities(context, user, [ENTITY_TYPE_CONTAINER], { ...args, filters }); +}; + +export const containersTimeSeriesByAuthor = async (context, user, args) => { + const { authorId } = args; + const filters = addFilter(args.filters, buildRefRelationKey(RELATION_CREATED_BY, '*'), authorId); + return timeSeriesEntities(context, user, [ENTITY_TYPE_CONTAINER], { ...args, filters }); +}; + +export const containersNumberByEntity = (context, user, args) => { + const { objectId } = args; + const filters = addFilter(args.filters, buildRefRelationKey(RELATION_OBJECT, '*'), objectId); + return { + count: elCount( + context, + user, + READ_INDEX_STIX_DOMAIN_OBJECTS, + { ...args, filters, types: [ENTITY_TYPE_CONTAINER] }, + ), + total: elCount( + context, + user, + READ_INDEX_STIX_DOMAIN_OBJECTS, + { ...R.dissoc('endDate', args), filters, types: [ENTITY_TYPE_CONTAINER] }, + ), + }; +}; + +export const containersNumberByAuthor = (context, user, args) => { + const { authorId } = args; + const filters = addFilter(args.filters, buildRefRelationKey(RELATION_CREATED_BY, '*'), authorId); + return { + count: elCount( + context, + user, + READ_INDEX_STIX_DOMAIN_OBJECTS, + { ...args, filters, types: [ENTITY_TYPE_CONTAINER] }, + ), + total: elCount( + context, + user, + READ_INDEX_STIX_DOMAIN_OBJECTS, + { ...R.dissoc('endDate', args), filters, types: [ENTITY_TYPE_CONTAINER] }, + ), + }; +}; + // List first 1000 objects of this container // Then find the containers that contains also the resolved objects export const relatedContainers = async (context, user, containerId, args) => { diff --git a/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js b/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js index 00058df077f6..444a5e76b0f8 100644 --- a/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js +++ b/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js @@ -10,6 +10,7 @@ import { findById as findStatusById } from './status'; import { ABSTRACT_BASIC_OBJECT, ABSTRACT_STIX_CORE_OBJECT, + ABSTRACT_STIX_CORE_RELATIONSHIP, ABSTRACT_STIX_DOMAIN_OBJECT, buildRefRelationKey, CONNECTOR_INTERNAL_ANALYSIS, @@ -37,7 +38,7 @@ import { elCount, elUpdateElement } from '../database/engine'; import { generateStandardId, getInstanceIds } from '../schema/identifier'; import { askEntityExport, askListExport, exportTransformFilters } from './stix'; import { isEmptyField, isNotEmptyField, READ_ENTITIES_INDICES, READ_INDEX_INFERRED_ENTITIES } from '../database/utils'; -import { RELATION_RELATED_TO, STIX_CORE_RELATIONSHIPS } from '../schema/stixCoreRelationship'; +import { STIX_CORE_RELATIONSHIPS } from '../schema/stixCoreRelationship'; import { ENTITY_TYPE_CONTAINER_CASE } from '../modules/case/case-types'; import { getEntitySettingFromCache } from '../modules/entitySetting/entitySetting-utils'; import { stixObjectOrRelationshipAddRefRelation, stixObjectOrRelationshipAddRefRelations, stixObjectOrRelationshipDeleteRefRelation } from './stixObjectOrStixRelationship'; @@ -432,7 +433,11 @@ export const stixCoreObjectsDistribution = async (context, user, args) => { }; export const stixCoreObjectsDistributionByEntity = async (context, user, args) => { - const { objectId, types, filters } = args; + const { objectId, types, filters = { + mode: 'and', + filters: [], + filterGroups: [], + } } = args; let finalFilters = filters; // Here, we need to force regardingOf ID = objectID // Check if filter is already present and replace id @@ -451,7 +456,7 @@ export const stixCoreObjectsDistributionByEntity = async (context, user, args) = } else { finalFilters = addFilter(filters, INSTANCE_REGARDING_OF, [ { key: 'id', values: [objectId] }, - { key: 'type', values: [RELATION_RELATED_TO] } + { key: 'type', values: [ABSTRACT_STIX_CORE_RELATIONSHIP] } ]); } return distributionEntities(context, user, types ?? [ABSTRACT_STIX_CORE_OBJECT], { ...args, filters: finalFilters }); diff --git a/opencti-platform/opencti-graphql/src/generated/graphql.ts b/opencti-platform/opencti-graphql/src/generated/graphql.ts index 69b5e97d31ed..2f39cb0c99f8 100644 --- a/opencti-platform/opencti-graphql/src/generated/graphql.ts +++ b/opencti-platform/opencti-graphql/src/generated/graphql.ts @@ -17847,6 +17847,7 @@ export type Query = { connectorsForWorker?: Maybe>>; container?: Maybe; containers?: Maybe; + containersNumber?: Maybe; containersObjectsOfObject?: Maybe; countries?: Maybe; country?: Maybe; @@ -18416,6 +18417,12 @@ export type QueryContainersArgs = { }; +export type QueryContainersNumberArgs = { + endDate?: InputMaybe; + objectId?: InputMaybe; +}; + + export type QueryContainersObjectsOfObjectArgs = { filters?: InputMaybe; id: Scalars['String']['input']; @@ -35844,6 +35851,7 @@ export type QueryResolvers>>, ParentType, ContextType>; container?: Resolver, ParentType, ContextType, Partial>; containers?: Resolver, ParentType, ContextType, Partial>; + containersNumber?: Resolver, ParentType, ContextType, Partial>; containersObjectsOfObject?: Resolver, ParentType, ContextType, RequireFields>; countries?: Resolver, ParentType, ContextType, Partial>; country?: Resolver, ParentType, ContextType, Partial>; diff --git a/opencti-platform/opencti-graphql/src/resolvers/container.js b/opencti-platform/opencti-graphql/src/resolvers/container.js index 6bde1b3ee6d6..09dc5ff5a6a7 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/container.js +++ b/opencti-platform/opencti-graphql/src/resolvers/container.js @@ -1,4 +1,14 @@ -import { containersObjectsOfObject, findAll, findById, knowledgeAddFromInvestigation, objects, relatedContainers } from '../domain/container'; +import { + containersObjectsOfObject, + findAll, + findById, + knowledgeAddFromInvestigation, + objects, + relatedContainers, + containersNumber, + containersNumberByAuthor, + containersNumberByEntity +} from '../domain/container'; import { stixDomainObjectAddRelation, stixDomainObjectCleanContext, @@ -14,6 +24,15 @@ const containerResolvers = { container: (_, { id }, context) => findById(context, context.user, id), containers: (_, args, context) => findAll(context, context.user, args), containersObjectsOfObject: (_, args, context) => containersObjectsOfObject(context, context.user, args), + containersNumber: (_, args, context) => { + if (args.objectId && args.objectId.length > 0) { + return containersNumberByEntity(context, context.user, args); + } + if (args.authorId && args.authorId.length > 0) { + return containersNumberByAuthor(context, context.user, args); + } + return containersNumber(context, context.user, args); + }, }, Container: { __resolveType(obj) {