From cfa63437f2003b016bc9196239c4072674b28229 Mon Sep 17 00:00:00 2001 From: Romy Alula Date: Thu, 1 Feb 2024 15:37:54 +0100 Subject: [PATCH] [backend/frontend] Enable CSV Feed Ingester (#4569) (#5404) Co-authored-by: BocognanoSarah Co-authored-by: Laurent Bonnet <146674147+labo-flg@users.noreply.github.com> --- .../opencti-front/lang/front/de.json | 13 +- .../opencti-front/lang/front/en.json | 12 +- .../opencti-front/lang/front/es.json | 12 +- .../opencti-front/lang/front/fr.json | 12 +- .../opencti-front/lang/front/ja.json | 12 +- .../opencti-front/lang/front/zh.json | 12 +- .../src/components/AutocompleteField.jsx | 4 + .../src/components/DateTimePickerField.jsx | 2 +- .../opencti-front/src/components/ItemIcon.jsx | 3 + .../components/common/form/CreatorField.tsx | 3 + .../components/common/form/CsvMapperField.tsx | 92 +++++ .../private/components/data/IngestionCsv.tsx | 129 ++++++ .../private/components/data/IngestionMenu.tsx | 4 + .../src/private/components/data/Root.jsx | 11 + .../data/csvMapper/CsvMapperForm.tsx | 56 +-- .../ingestionCsv/IngestionCsvCreation.tsx | 344 ++++++++++++++++ .../data/ingestionCsv/IngestionCsvEdition.tsx | 373 ++++++++++++++++++ .../IngestionCsvEditionContainer.tsx | 54 +++ .../data/ingestionCsv/IngestionCsvLine.tsx | 188 +++++++++ .../data/ingestionCsv/IngestionCsvLines.tsx | 109 +++++ .../IngestionCsvMapperTestDialog.tsx | 154 ++++++++ .../data/ingestionCsv/IngestionCsvPopover.tsx | 265 +++++++++++++ .../src/schema/relay.schema.graphql | 63 +++ .../opencti-front/src/utils/Localization.js | 0 .../opencti-front/src/utils/edition.js | 9 + .../opencti-graphql/graphql-codegen.yml | 1 + .../opencti-graphql/src/domain/work.js | 2 +- .../opencti-graphql/src/generated/graphql.ts | 166 +++++++- .../src/manager/ingestionManager.ts | 105 ++++- .../opencti-graphql/src/modules/index.ts | 2 + .../modules/ingestion/ingestion-converter.ts | 20 +- .../modules/ingestion/ingestion-csv-domain.ts | 112 ++++++ .../ingestion/ingestion-csv-graphql.ts | 8 + .../ingestion/ingestion-csv-resolver.ts | 39 ++ .../modules/ingestion/ingestion-csv.graphql | 81 ++++ .../src/modules/ingestion/ingestion-csv.ts | 42 ++ .../src/modules/ingestion/ingestion-types.ts | 37 ++ .../internal/csvMapper/csvMapper-utils.ts | 2 +- .../opencti-graphql/src/parser/csv-parser.ts | 5 +- .../opencti-graphql/src/types/work.d.ts | 22 ++ .../02-resolvers/ingestion-csv-test.ts | 167 ++++++++ 41 files changed, 2699 insertions(+), 48 deletions(-) create mode 100644 opencti-platform/opencti-front/src/private/components/common/form/CsvMapperField.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/IngestionCsv.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvCreation.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEdition.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEditionContainer.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLine.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLines.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvMapperTestDialog.tsx create mode 100644 opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvPopover.tsx create mode 100644 opencti-platform/opencti-front/src/utils/Localization.js create mode 100644 opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-domain.ts create mode 100644 opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-graphql.ts create mode 100644 opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-resolver.ts create mode 100644 opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql create mode 100644 opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts create mode 100644 opencti-platform/opencti-graphql/src/types/work.d.ts create mode 100644 opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/ingestion-csv-test.ts diff --git a/opencti-platform/opencti-front/lang/front/de.json b/opencti-platform/opencti-front/lang/front/de.json index faa58f9c10609..91e8c129858d3 100644 --- a/opencti-platform/opencti-front/lang/front/de.json +++ b/opencti-platform/opencti-front/lang/front/de.json @@ -2436,7 +2436,6 @@ "Do you want to unlink this task ?": "Möchten Sie die Verknüpfung dieser Aufgabe aufheben?", "CREATE": "CREATE", "entity_Analyses": "entity_Analyses", - "workflow_id": "Status", "regardingOf": "In Bezug auf", "WITH": "MIT", "Pre-query to get data to be used as source entity of the relationship (limited to 5000)": "Pre-Query, um Daten zu erhalten, die als Quell-Entität der Beziehung verwendet werden sollen (begrenzt auf 5000)", @@ -2501,6 +2500,13 @@ "The tag has been removed": "Das Tag wurde entfernt", "I have read and comply with the above statement": "Ich habe die obige Erklärung gelesen und erkläre mich damit einverstanden", "You need to activate OpenCTI enterprise edition to use this feature.": "Um diese Funktion nutzen zu können, müssen Sie die OpenCTI Enterprise Edition aktivieren.", + "workflow_id": "Status", + "Do you want to delete this CSV ingester?": "Möchten Sie diesen CSV-Importer löschen?", + "Do you want to start this CSV ingester?": "Möchten Sie diesen CSV-Importer starten?", + "Do you want to stop this CSV ingester?": "Möchten Sie diesen CSV-Importer stoppen?", + "Create a CSV Ingester": "Einen CSV-Importer erstellen", + "Update a CSV Ingester": "Einen CSV-Importer aktualisieren", + "Please, note that the test will be run on the 50 first lines": "Bitte beachten Sie, dass der Test auf den ersten 50 Zeilen durchgeführt wird", "x_opencti_main_observable_type": "Wichtigster beobachtbarer Typ", "Update subscription": "Update Abonnement", "Subscribe to updates (modifications and new relations)": "Abonnement für Aktualisierungen (Änderungen und neue Beziehungen)", @@ -2556,5 +2562,8 @@ "Marking definitions to use by the csv mapper...": "Markierungsdefinitionen, die vom csv-Mapper nur verwendet werden, wenn für einige Markierungsattribute die Richtlinie 'Der Benutzer darf die Markierungsdefinitionen auswählen' festgelegt ist. Andernfalls wird diese Markierungsdefinition ignoriert.", "Option 'Let the user choose marking definitions'...": "Die Option 'Der Benutzer darf die Markierungsdefinitionen auswählen' fordert den Benutzer auf, beim Importieren der CSV-Datei auszuwählen, welche Markierungsdefinitionen angewendet werden sollen, wenn die Daten in der CSV-Datei fehlen.", "Update without references": "Update ohne Referenzen", - "Max Confidence Level:": "Max. Konfidenzniveau:" + "Max Confidence Level:": "Max. Konfidenzniveau:", + "Please, verify the validity of the selected CSV mapper for the given URL.": "Bitte überprüfen Sie die Gültigkeit des ausgewählten CSV-Mappers für die angegebene URL.", + "Only successful tests allow the ingestion creation.": "Nur erfolgreiche Tests ermöglichen die Erstellung der Aufnahme.", + "Only successful tests allow the ingestion edition.": "Nur erfolgreiche Tests erlauben die Ingestion-Edition." } \ No newline at end of file diff --git a/opencti-platform/opencti-front/lang/front/en.json b/opencti-platform/opencti-front/lang/front/en.json index d6f94b69274ed..e25062452ba67 100644 --- a/opencti-platform/opencti-front/lang/front/en.json +++ b/opencti-platform/opencti-front/lang/front/en.json @@ -2447,6 +2447,12 @@ "CREATE": "CREATE", "entity_Analyses": "entity_Analyses", "workflow_id": "Status", + "Do you want to delete this CSV ingester?": "Do you want to delete this CSV ingester?", + "Do you want to start this CSV ingester?": "Do you want to start this CSV ingester?", + "Do you want to stop this CSV ingester?": "Do you want to stop this CSV ingester?", + "Create a CSV Ingester": "Create a CSV Ingester", + "Update a CSV Ingester": "Update a CSV Ingester", + "Please, note that the test will be run on the 50 first lines": "Please, note that the test will be run on the 50 first lines", "regardingOf": "In regards of", "Uploading image": "Uploading image", "Paste": "Paste", @@ -2556,5 +2562,9 @@ "Marking definitions to use by the csv mapper...": "Marking definitions used by the csv mapper only when some marking attributes have the policy set to 'Let the user choose the marking definitions'. If not this Marking definitions is ignored.", "Option 'Let the user choose marking definitions'...": "Option 'Let the user choose marking definitions' will ask the user to choose (when importing the CSV file) which marking definitions to apply when the data is missing in the CSV file.", "Update without references": "Update without references", - "Max Confidence Level:": "Max Confidence Level:" + "Max Confidence Level:": "Max Confidence Level:", + "Settings default values": "Default value from entity settings: {value}", + "Please, verify the validity of the selected CSV mapper for the given URL.": "Please, verify the validity of the selected CSV mapper for the given URL.", + "Only successful tests allow the ingestion creation.": "Only successful tests allow the ingestion creation.", + "Only successful tests allow the ingestion edition.": "Only successful tests allow the ingestion edition." } \ No newline at end of file diff --git a/opencti-platform/opencti-front/lang/front/es.json b/opencti-platform/opencti-front/lang/front/es.json index 52f6e728ac6a3..538624d546b99 100644 --- a/opencti-platform/opencti-front/lang/front/es.json +++ b/opencti-platform/opencti-front/lang/front/es.json @@ -2437,6 +2437,12 @@ "entity_Analyses": "entity_Analyses", "There are no opinions. You should create some before.": "No hay opiniones. Deberías crear algunas antes.", "workflow_id": "Estado", + "Do you want to delete this CSV ingester?": "¿Quieres eliminar este importador CSV?", + "Do you want to start this CSV ingester?": "¿Quieres iniciar este importador CSV?", + "Do you want to stop this CSV ingester?": "¿Quieres detener este importador CSV?", + "Create a CSV Ingester": "Crear un Importador CSV", + "Update a CSV Ingester": "Actualizar un Importador CSV", + "Please, note that the test will be run on the 50 first lines": "Por favor, ten en cuenta que la prueba se ejecutará en las primeras 50 líneas", "regardingOf": "En cuanto a", "WITH": "CON", "Pre-query to get data to be used as source entity of the relationship (limited to 5000)": "Consulta previa para obtener los datos que se utilizarán como entidad fuente de la relación (limitada a 5000)", @@ -2556,5 +2562,9 @@ "Marking definitions to use by the csv mapper...": "Definiciones de marcado utilizadas por el mapeador csv sólo cuando algunos atributos de marcado tienen la política establecida en 'Dejar que el usuario elija las definiciones de marcado'. Si no, estas definiciones de marcado se ignoran.", "Option 'Let the user choose marking definitions'...": "La opción 'Dejar que el usuario elija las definiciones de marcado' pedirá al usuario que elija (al importar el archivo CSV) qué definiciones de marcado aplicar cuando falten datos en el archivo CSV.", "Update without references": "Actualizar sin referencias", - "Max Confidence Level:": "Nivel Máximo de Confianza:" + "Max Confidence Level:": "Nivel Máximo de Confianza:", + "The Max Confidence Level is currently defined at the user level. It overrides Max Confidence Level from user’s groups.": "El nivel de confianza máximo se define actualmente a nivel de usuario. Anula el nivel de confianza máximo de los grupos de usuarios.", + "Please, verify the validity of the selected CSV mapper for the given URL.": "Por favor, verifique la validez del asignador CSV seleccionado para la URL proporcionada.", + "Only successful tests allow the ingestion creation.": "Sólo las pruebas exitosas permiten la creación de la ingesta.", + "Only successful tests allow the ingestion edition.": "Sólo las pruebas exitosas permiten la edición de ingesta." } \ No newline at end of file diff --git a/opencti-platform/opencti-front/lang/front/fr.json b/opencti-platform/opencti-front/lang/front/fr.json index b7e48fec65cc7..dcbf32cb0c153 100644 --- a/opencti-platform/opencti-front/lang/front/fr.json +++ b/opencti-platform/opencti-front/lang/front/fr.json @@ -2437,6 +2437,12 @@ "valid": "valide", "There are no opinions. You should create some before.": "Il n'y a pas d'd’opinions. Vous devriez en créer avant.", "workflow_id": "Statut", + "Do you want to delete this CSV ingester?": "Voulez-vous supprimer cet ingéreur CSV ?", + "Do you want to start this CSV ingester?": "Voulez-vous démarrer cet ingéreur CSV ?", + "Do you want to stop this CSV ingester?": "Voulez-vous arrêter cet ingéreur CSV ?", + "Create a CSV Ingester": "Créer un ingéreur CSV", + "Update a CSV Ingester": "Mettre à jour un ingéreur CSV", + "Please, note that the test will be run on the 50 first lines": "Veuillez noter que le test sera effectué sur les 50 premières lignes", "regardingOf": "Concernant", "WITH": "AVEC", "Pre-query to get data to be used as source entity of the relationship (limited to 5000)": "Pré-requête pour obtenir les données à utiliser comme entité source de la relation (limité à 5000)", @@ -2556,5 +2562,9 @@ "Marking definitions to use by the csv mapper...": "Les définitions de marquage ne sont utilisées par le mappeur csv que lorsque certains attributs de marquage ont la politique 'Laisser l'utilisateur choisir les définitions de marquage'. Dans le cas contraire, ces définitions de marquage sont ignorées.", "Option 'Let the user choose marking definitions'...": "L'option 'Let the user choose marking definitions' demandera à l'utilisateur de choisir (lors de l'importation du fichier CSV) les définitions de marquage à appliquer lorsque les données sont manquantes dans le fichier CSV.", "Update without references": "Mise à jour sans références", - "Max Confidence Level:": "Niveau de confiance maximum :" + "Max Confidence Level:": "Niveau de confiance maximum :", + "The Max Confidence Level is currently defined at the user level. It overrides Max Confidence Level from user’s groups.": "Le niveau de confiance maximum est actuellement défini au niveau de l'utilisateur. Il est prioritaire sur le niveau de confiance maximum des groupes d'utilisateurs.", + "Please, verify the validity of the selected CSV mapper for the given URL.": "Veuillez vérifier la validité du mappeur CSV sélectionné pour l'URL donnée.", + "Only successful tests allow the ingestion creation.": "Seuls les tests réussis permettent la création de l'ingestion.", + "Only successful tests allow the ingestion edition.": "Seuls les tests réussis permettent l'édition de l'ingestion." } \ No newline at end of file diff --git a/opencti-platform/opencti-front/lang/front/ja.json b/opencti-platform/opencti-front/lang/front/ja.json index b80f1d9ba38a4..a2326b40984b3 100644 --- a/opencti-platform/opencti-front/lang/front/ja.json +++ b/opencti-platform/opencti-front/lang/front/ja.json @@ -2437,6 +2437,12 @@ "entity_Analyses": "エンティティ_分析", "There are no opinions. You should create some before.": "意見がない。事前にいくつか作っておくべきだ。", "workflow_id": "ステータス", + "Do you want to delete this CSV ingester?": "このCSVインガスターを削除しますか?", + "Do you want to start this CSV ingester?": "このCSVインガスターを開始しますか?", + "Do you want to stop this CSV ingester?": "このCSVインガスターを停止しますか?", + "Create a CSV Ingester": "CSVインガスターを作成する", + "Update a CSV Ingester": "CSVインガスターを更新する", + "Please, note that the test will be run on the 50 first lines": "50行目までのテストを実行することに注意してください", "regardingOf": "に関しては", "WITH": "WITH", "Pre-query to get data to be used as source entity of the relationship (limited to 5000)": "リレーションシップのソース・エンティティとして使用されるデータを取得するための事前クエリ(5000個まで)", @@ -2556,5 +2562,9 @@ "Marking definitions to use by the csv mapper...": "いくつかのマーキング属性が'ユーザーにマーキング定義を選択させる'にポリシーが設定されている場合にのみ、csvマッパーはマーキング定義を使用します。そうでない場合、このマーキング定義は無視されます。", "Option 'Let the user choose marking definitions'...": "オプション 'ユーザーにマーキング定義を選択させる' は、CSVファイルにデータがない場合に適用するマーキング定義を(CSVファイルのインポート時に)ユーザーに選択させます。", "Update without references": "参照なしで更新", - "Max Confidence Level:": "最大信頼度:" + "Max Confidence Level:": "最大信頼度:", + "The Max Confidence Level is currently defined at the user level. It overrides Max Confidence Level from user’s groups.": "最大信頼度は現在ユーザーレベルで定義されています。これはユーザーグループの最大信頼度より優先されます。", + "Please, verify the validity of the selected CSV mapper for the given URL.": "指定された URL に対して選択した CSV マッパーが有効であることを確認してください。", + "Only successful tests allow the ingestion creation.": "成功したテストのみが取り込みの作成を許可します。", + "Only successful tests allow the ingestion edition.": "テストが成功した場合のみ、インジェスト エディションが許可されます。" } \ No newline at end of file diff --git a/opencti-platform/opencti-front/lang/front/zh.json b/opencti-platform/opencti-front/lang/front/zh.json index a389c57ff3aa4..855f239a11a73 100644 --- a/opencti-platform/opencti-front/lang/front/zh.json +++ b/opencti-platform/opencti-front/lang/front/zh.json @@ -2437,6 +2437,12 @@ "entity_Analyses": "实体_分析", "There are no opinions. You should create some before.": "没有意见。你应该先提出一些意见。", "workflow_id": "状态", + "Do you want to delete this CSV ingester?": "您要删除此CSV导入程序吗?", + "Do you want to start this CSV ingester?": "您要启动此CSV导入程序吗?", + "Do you want to stop this CSV ingester?": "您要停止此CSV导入程序吗?", + "Create a CSV Ingester": "创建一个CSV导入程序", + "Update a CSV Ingester": "更新一个CSV导入程序", + "Please, note that the test will be run on the 50 first lines": "请注意,测试将在前50行运行", "regardingOf": "关于", "WITH": "有", "Pre-query to get data to be used as source entity of the relationship (limited to 5000)": "预查询,获取用作关系源实体的数据(限制为 5000)", @@ -2556,5 +2562,9 @@ "Marking definitions to use by the csv mapper...": "只有当某些标记属性的策略设置为'让用户选择标记定义'时,csv 映射器才会使用标记定义。否则,此标记定义将被忽略。", "Option 'Let the user choose marking definitions'...": "选项 '让用户选择标记定义'将要求用户(在导入 CSV 文件时)选择在 CSV 文件中缺少数据时应用哪些标记定义。", "Update without references": "无引用更新", - "Max Confidence Level:": "最大置信度:" + "Max Confidence Level:": "最大置信度:", + "The Max Confidence Level is currently defined at the user level. It overrides Max Confidence Level from user’s groups.": "当前最大置信水平是在用户级别定义的。它优先于用户组的最大置信度。", + "Please, verify the validity of the selected CSV mapper for the given URL.": "请验证给定 URL 所选 CSV 映射器的有效性。", + "Only successful tests allow the ingestion creation.": "只有成功的测试才允许创建摄取。", + "Only successful tests allow the ingestion edition.": "只有成功的测试才允许摄取版本。" } \ No newline at end of file diff --git a/opencti-platform/opencti-front/src/components/AutocompleteField.jsx b/opencti-platform/opencti-front/src/components/AutocompleteField.jsx index a4edb3dea24fe..dc28bcebded71 100644 --- a/opencti-platform/opencti-front/src/components/AutocompleteField.jsx +++ b/opencti-platform/opencti-front/src/components/AutocompleteField.jsx @@ -47,6 +47,10 @@ const AutocompleteField = (props) => { const fieldProps = fieldToAutocomplete(props); delete fieldProps.helperText; delete fieldProps.openCreate; + // Properly handle no selected option + if (fieldProps.value === '') { + fieldProps.value = null; + } const defaultOptionToValue = (option, value) => option.value === value.value; return (
diff --git a/opencti-platform/opencti-front/src/components/DateTimePickerField.jsx b/opencti-platform/opencti-front/src/components/DateTimePickerField.jsx index 9857890453fdf..b6743fcdc98e2 100644 --- a/opencti-platform/opencti-front/src/components/DateTimePickerField.jsx +++ b/opencti-platform/opencti-front/src/components/DateTimePickerField.jsx @@ -107,7 +107,7 @@ const DateTimePickerField = (props) => { onFocus: internalOnFocus, onBlur: internalOnBlur, error: !R.isNil(meta.error), - helperText: (!R.isNil(meta.error) && meta.error) || textFieldProps.helperText, + helperText: (!R.isNil(meta.error) && meta.error) || (textFieldProps.helperText ?? ''), }, }} /> diff --git a/opencti-platform/opencti-front/src/components/ItemIcon.jsx b/opencti-platform/opencti-front/src/components/ItemIcon.jsx index 94bcc1bd35f91..fa834a9e71eac 100644 --- a/opencti-platform/opencti-front/src/components/ItemIcon.jsx +++ b/opencti-platform/opencti-front/src/components/ItemIcon.jsx @@ -93,6 +93,7 @@ import { SourcePull, Target, } from 'mdi-material-ui'; +import TableViewIcon from '@mui/icons-material/TableView'; import { itemColor } from '../utils/Colors'; const iconSelector = (type, variant, fontSize, color, isReversed) => { @@ -158,6 +159,8 @@ const iconSelector = (type, variant, fontSize, color, isReversed) => { ); case 'work': + case 'csvmapper': + return ; case 'connector': return ; case 'marking-definition': diff --git a/opencti-platform/opencti-front/src/private/components/common/form/CreatorField.tsx b/opencti-platform/opencti-front/src/private/components/common/form/CreatorField.tsx index d7e0f57560000..e090a23649e55 100644 --- a/opencti-platform/opencti-front/src/private/components/common/form/CreatorField.tsx +++ b/opencti-platform/opencti-front/src/private/components/common/form/CreatorField.tsx @@ -27,6 +27,7 @@ const useStyles = makeStyles(() => ({ interface CreatorFieldProps { name: string; label: string; + isOptionEqualToValue?: (option: Option, value: string) => boolean; onChange?: (name: string, value: Option) => void; containerStyle?: Record; helpertext?: string; @@ -49,6 +50,7 @@ const CreatorField: FunctionComponent = ({ name, label, containerStyle, + isOptionEqualToValue, onChange, helpertext, }) => { @@ -99,6 +101,7 @@ const CreatorField: FunctionComponent = ({ style={containerStyle} noOptionsText={t_i18n('No available options')} options={creators} + isOptionEqualToValue={isOptionEqualToValue} onInputChange={searchCreators} renderOption={( props: React.HTMLAttributes, diff --git a/opencti-platform/opencti-front/src/private/components/common/form/CsvMapperField.tsx b/opencti-platform/opencti-front/src/private/components/common/form/CsvMapperField.tsx new file mode 100644 index 0000000000000..828035bcf9ffa --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/common/form/CsvMapperField.tsx @@ -0,0 +1,92 @@ +import { Option } from '@components/common/form/ReferenceField'; +import { graphql, PreloadedQuery, usePreloadedQuery } from 'react-relay'; +import React, { FunctionComponent } from 'react'; +import makeStyles from '@mui/styles/makeStyles'; +import { Field } from 'formik'; +import { CsvMapperFieldSearchQuery } from '@components/common/form/__generated__/CsvMapperFieldSearchQuery.graphql'; +import { useFormatter } from '../../../../components/i18n'; +import AutocompleteField from '../../../../components/AutocompleteField'; +import { fieldSpacingContainerStyle } from '../../../../utils/field'; +import ItemIcon from '../../../../components/ItemIcon'; + +const useStyles = makeStyles(() => ({ + icon: { + paddingTop: 4, + display: 'inline-block', + }, + text: { + display: 'inline-block', + flexGrow: 1, + marginLeft: 10, + }, + autoCompleteIndicator: { + display: 'none', + }, +})); + +interface CsvMapperFieldComponentProps { + name: string; + isOptionEqualToValue: (option: Option, value: Option) => boolean; + onChange?: (name: string, value: Option) => void; + queryRef: PreloadedQuery +} + +export const csvMapperQuery = graphql` + query CsvMapperFieldSearchQuery($search: String) { + csvMappers(search: $search) { + edges { + node { + id + name + } + } + } + } +`; + +const CsvMapperField: FunctionComponent = ({ + onChange, + isOptionEqualToValue, + name, + queryRef, +}) => { + const classes = useStyles(); + const { t_i18n } = useFormatter(); + const data = usePreloadedQuery(csvMapperQuery, queryRef); + const csvMappersPreloaded = (data?.csvMappers?.edges || []).map(({ node }) => ({ + value: node.id, + label: node.name, + })); + return ( + <> + , + option: Option, + ) => ( +
  • +
    + +
    +
    {option.label}
    +
  • + )} + /> + + ); +}; + +export default CsvMapperField; diff --git a/opencti-platform/opencti-front/src/private/components/data/IngestionCsv.tsx b/opencti-platform/opencti-front/src/private/components/data/IngestionCsv.tsx new file mode 100644 index 0000000000000..500141ea064b1 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/IngestionCsv.tsx @@ -0,0 +1,129 @@ +import makeStyles from '@mui/styles/makeStyles'; +import Alert from '@mui/material/Alert'; +import React from 'react'; +import IngestionMenu from '@components/data/IngestionMenu'; +import IngestionCsvLines, { ingestionCsvLinesQuery } from '@components/data/ingestionCsv/IngestionCsvLines'; +import { IngestionCsvLinesPaginationQuery, IngestionCsvLinesPaginationQuery$variables } from '@components/data/ingestionCsv/__generated__/IngestionCsvLinesPaginationQuery.graphql'; +import { IngestionCsvLineDummy } from '@components/data/ingestionCsv/IngestionCsvLine'; +import IngestionCsvCreation from '@components/data/ingestionCsv/IngestionCsvCreation'; +import { useFormatter } from '../../../components/i18n'; +import useAuth from '../../../utils/hooks/useAuth'; +import { usePaginationLocalStorage } from '../../../utils/hooks/useLocalStorage'; +import { INGESTION_MANAGER } from '../../../utils/platformModulesHelper'; +import ListLines from '../../../components/list_lines/ListLines'; +import useQueryLoading from '../../../utils/hooks/useQueryLoading'; +import { KNOWLEDGE_KNUPDATE } from '../../../utils/hooks/useGranted'; +import Security from '../../../utils/Security'; + +const LOCAL_STORAGE_KEY = 'ingestionCsvs'; + +const useStyles = makeStyles(() => ({ + container: { + margin: 0, + padding: '0 200px 50px 0', + }, +})); + +const IngestionCsv = () => { + const classes = useStyles(); + const { t_i18n } = useFormatter(); + const { platformModuleHelpers } = useAuth(); + const { + viewStorage, + paginationOptions, + helpers, + } = usePaginationLocalStorage(LOCAL_STORAGE_KEY, { + sortBy: 'name', + orderAsc: false, + searchTerm: '', + numberOfElements: { + number: 0, + symbol: '', + }, + }); + const renderLines = () => { + const { searchTerm, sortBy, orderAsc, numberOfElements } = viewStorage; + const dataColumns = { + name: { + label: 'Name', + width: '20%', + isSortable: true, + }, + uri: { + label: 'URL', + width: '30%', + isSortable: true, + }, + ingestion_running: { + label: 'Running', + width: '20%', + isSortable: false, + }, + current_state_date: { + label: 'Current state', + isSortable: false, + width: '15%', + }, + }; + const queryRef = useQueryLoading( + ingestionCsvLinesQuery, + paginationOptions, + ); + return ( + + {queryRef && ( + + {Array(20) + .fill(0) + .map((_, idx) => ( + + ))} + + } + > + + + )} + + ); + }; + if (!platformModuleHelpers.isIngestionManagerEnable()) { + return ( + + {t_i18n(platformModuleHelpers.generateDisableMessage(INGESTION_MANAGER))} + + ); + } + return ( +
    + + <> + {renderLines()} + + + + +
    + ); +}; + +export default IngestionCsv; diff --git a/opencti-platform/opencti-front/src/private/components/data/IngestionMenu.tsx b/opencti-platform/opencti-front/src/private/components/data/IngestionMenu.tsx index 9a9b6f77a4165..26c41fe75a3a4 100644 --- a/opencti-platform/opencti-front/src/private/components/data/IngestionMenu.tsx +++ b/opencti-platform/opencti-front/src/private/components/data/IngestionMenu.tsx @@ -15,6 +15,10 @@ const IngestionMenu = () => { path: '/dashboard/data/ingestion/rss', label: 'RSS Feeds', }, + { + path: '/dashboard/data/ingestion/csv', + label: 'CSV Feeds', + }, ]; return ; }; diff --git a/opencti-platform/opencti-front/src/private/components/data/Root.jsx b/opencti-platform/opencti-front/src/private/components/data/Root.jsx index 736b04a3bbb74..f4b9f195f7df4 100644 --- a/opencti-platform/opencti-front/src/private/components/data/Root.jsx +++ b/opencti-platform/opencti-front/src/private/components/data/Root.jsx @@ -7,6 +7,7 @@ import Loader from '../../../components/Loader'; const CsvMappers = lazy(() => import('./CsvMappers')); const Security = lazy(() => import('../../../utils/Security')); const Connectors = lazy(() => import('./Connectors')); +const IngestionCsv = lazy(() => import('./IngestionCsv')); const Entities = lazy(() => import('./Entities')); const Relationships = lazy(() => import('./Relationships')); const Tasks = lazy(() => import('./Tasks')); @@ -68,6 +69,16 @@ const Root = () => { path="/dashboard/data/ingestion/taxii" component={IngestionTaxiis} /> + + ((theme) => ({ }, })); -const csvMapperValidation = (t: (s: string) => string) => Yup.object().shape({ - name: Yup.string().required(t('This field is required')), - has_header: Yup.boolean().required(t('This field is required')), - separator: Yup.string().required(t('This field is required')), +const csvMapperValidation = (t_i18n: (s: string) => string) => Yup.object().shape({ + name: Yup.string().required(t_i18n('This field is required')), + has_header: Yup.boolean().required(t_i18n('This field is required')), + separator: Yup.string().required(t_i18n('This field is required')), skipLineChar: Yup.string().max(1), }); @@ -191,30 +192,29 @@ const CsvMapperForm: FunctionComponent = ({ csvMapper, onSub
    {t_i18n('CSV separator')}
    - setFieldValue('separator', event.target.value) - } - /> - {t_i18n('Comma')} -
    -
    - setFieldValue('separator', event.target.value) - } - /> - {t_i18n('Semicolon')} + style={{ flexDirection: 'row' }} + value={values.separator} + onChange={(event: SelectChangeEvent) => setFieldValue('separator', event.target.value)} + > + } + label={t_i18n('Comma')} + /> + } + label={t_i18n('Semicolon')} + /> + } + label={t_i18n('Pipe')} + /> +
    ((theme) => ({ + buttons: { + marginTop: 20, + textAlign: 'right', + }, + button: { + marginLeft: theme.spacing(2), + }, +})); + +const ingestionCsvCreationMutation = graphql` + mutation IngestionCsvCreationMutation($input: IngestionCsvAddInput!) { + ingestionCsvAdd(input: $input) { + ...IngestionCsvLine_node + } + } +`; + +interface IngestionCsvCreationProps { + paginationOptions: IngestionCsvLinesPaginationQuery$variables; +} + +export interface IngestionCsvCreationForm { + name: string + description: string + uri: string + csv_mapper_id: string | Option + authentication_type: CsvAuthType + authentication_value: string + current_state_date: Date | null + user_id: string | Option + username?: string + password?: string + cert?: string + key?: string + ca?: string +} + +const IngestionCsvCreation: FunctionComponent = ({ paginationOptions }) => { + const { t_i18n } = useFormatter(); + const classes = useStyles(); + const [open, setOpen] = useState(false); + const [isCreateDisabled, setIsCreateDisabled] = useState(true); + + const ingestionCsvCreationValidation = () => Yup.object().shape({ + name: Yup.string().required(t_i18n('This field is required')), + description: Yup.string().nullable(), + uri: Yup.string().required(t_i18n('This field is required')), + authentication_type: Yup.string().required(t_i18n('This field is required')), + authentication_value: Yup.string().nullable(), + current_state_date: Yup.date() + .typeError(t_i18n('The value must be a datetime (yyyy-MM-dd hh:mm (a|p)m)')) + .nullable(), + csv_mapper_id: Yup.object().required(t_i18n('This field is required')), + username: Yup.string().nullable(), + password: Yup.string().nullable(), + cert: Yup.string().nullable(), + key: Yup.string().nullable(), + ca: Yup.string().nullable(), + user_id: Yup.object().nullable(), + }); + + const [commit] = useMutation(ingestionCsvCreationMutation); + const onSubmit: FormikConfig['onSubmit'] = ( + values, + { setSubmitting, resetForm }, + ) => { + let authenticationValue = values.authentication_value; + if (values.authentication_type === 'basic') { + authenticationValue = `${values.username}:${values.password}`; + } else if (values.authentication_type === 'certificate') { + authenticationValue = `${values.cert}:${values.key}:${values.ca}`; + } + const userId = typeof values.user_id === 'string' ? values.user_id : values.user_id.value; + const input = { + name: values.name, + description: values.description, + uri: values.uri, + csv_mapper_id: typeof values.csv_mapper_id === 'string' ? values.csv_mapper_id : values.csv_mapper_id.value, + authentication_type: values.authentication_type, + authentication_value: authenticationValue, + current_state_date: values.current_state_date, + user_id: userId, + }; + commit({ + variables: { + input, + }, + updater: (store) => { + insertNode( + store, + 'Pagination_ingestionCsvs', + paginationOptions, + 'ingestionCsvAdd', + ); + }, + onCompleted: () => { + setSubmitting(false); + resetForm(); + }, + }); + }; + + const queryRef = useQueryLoading(csvMapperQuery); + + return ( + + {({ onClose }) => ( + + initialValues={{ + name: '', + description: '', + uri: '', + csv_mapper_id: '', + authentication_type: 'none', + authentication_value: '', + current_state_date: null, + user_id: '', + username: '', + password: '', + cert: '', + key: '', + ca: '', + }} + validationSchema={ingestionCsvCreationValidation} + onSubmit={onSubmit} + onReset={onClose} + > + {({ submitForm, handleReset, isSubmitting, values }) => ( +
    + + + + + { + queryRef && ( + }> + option.value === value} + queryRef={queryRef} + /> + + ) + } + + {t_i18n('None')} + + {t_i18n('Basic user / password')} + + {t_i18n('Bearer token')} + + {t_i18n('Client certificate')} + + + {values.authentication_type === 'basic' && ( + <> + + + + )} + {values.authentication_type === 'bearer' && ( + + )} + {values.authentication_type === 'certificate' && ( + <> + + + + + )} + option.value === value} + containerStyle={fieldSpacingContainerStyle} + /> + + + {t_i18n('Please, verify the validity of the selected CSV mapper for the given URL.')}
    + {t_i18n('Only successful tests allow the ingestion creation.')} +
    +
    +
    + + + +
    + setOpen(false)} + uri={values.uri} + csvMapperId={values.csv_mapper_id} + setIsCreateDisabled={setIsCreateDisabled} + /> + + )} + + )} +
    + ); +}; + +export default IngestionCsvCreation; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEdition.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEdition.tsx new file mode 100644 index 0000000000000..affde37f71d92 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEdition.tsx @@ -0,0 +1,373 @@ +import { graphql, useFragment, useMutation } from 'react-relay'; +import React, { FunctionComponent, useState } from 'react'; +import { Option } from '@components/common/form/ReferenceField'; +import * as Yup from 'yup'; +import { FormikConfig } from 'formik/dist/types'; +import { ExternalReferencesValues } from '@components/common/form/ExternalReferencesField'; +import { Field, Form, Formik } from 'formik'; +import MenuItem from '@mui/material/MenuItem'; +import Box from '@mui/material/Box'; +import Alert from '@mui/material/Alert'; +import CreatorField from '@components/common/form/CreatorField'; +import CommitMessage from '@components/common/form/CommitMessage'; +import { IngestionCsvEditionFragment_ingestionCsv$key } from '@components/data/ingestionCsv/__generated__/IngestionCsvEditionFragment_ingestionCsv.graphql'; +import CsvMapperField, { csvMapperQuery } from '@components/common/form/CsvMapperField'; +import Button from '@mui/material/Button'; +import IngestionCsvMapperTestDialog from '@components/data/ingestionCsv/IngestionCsvMapperTestDialog'; +import makeStyles from '@mui/styles/makeStyles'; +import { CsvMapperFieldSearchQuery } from '@components/common/form/__generated__/CsvMapperFieldSearchQuery.graphql'; +import { convertMapper, convertUser } from '../../../../utils/edition'; +import { useFormatter } from '../../../../components/i18n'; +import { useSchemaEditionValidation } from '../../../../utils/hooks/useEntitySettings'; +import { adaptFieldValue } from '../../../../utils/String'; +import TextField from '../../../../components/TextField'; +import { fieldSpacingContainerStyle } from '../../../../utils/field'; +import SelectField from '../../../../components/SelectField'; +import DateTimePickerField from '../../../../components/DateTimePickerField'; +import type { Theme } from '../../../../components/Theme'; +import useQueryLoading from '../../../../utils/hooks/useQueryLoading'; +import Loader, { LoaderVariant } from '../../../../components/Loader'; + +const useStyles = makeStyles((theme) => ({ + buttons: { + marginTop: 20, + textAlign: 'right', + }, + button: { + marginLeft: theme.spacing(2), + }, +})); + +export const ingestionCsvEditionPatch = graphql` + mutation IngestionCsvEditionPatchMutation($id: ID!, $input: [EditInput!]!) { + ingestionCsvFieldPatch(id: $id, input: $input) { + ...IngestionCsvEditionFragment_ingestionCsv + } + } +`; + +const ingestionCsvEditionFragment = graphql` + fragment IngestionCsvEditionFragment_ingestionCsv on IngestionCsv { + id + name + description + uri + authentication_type + authentication_value + ingestion_running + current_state_date + csvMapper { + id + name + } + user { + id + entity_type + name + } + } +`; + +interface IngestionCsvEditionProps { + ingestionCsv: IngestionCsvEditionFragment_ingestionCsv$key; + handleClose: () => void; + enableReferences?: boolean +} + +interface IngestionCsvEditionForm { + message?: string | null + references?: ExternalReferencesValues + name: string, + description?: string | null, + uri: string, + authentication_type: string, + authentication_value?: string | null, + current_state_date: Date | null + ingestion_running?: boolean | null, + csv_mapper_id: string | Option, + user_id: string | Option +} + +const IngestionCsvEdition: FunctionComponent = ({ + ingestionCsv, + handleClose, + enableReferences = false, +}) => { + const { t_i18n } = useFormatter(); + const classes = useStyles(); + const [open, setOpen] = useState(false); + const ingestionCsvData = useFragment(ingestionCsvEditionFragment, ingestionCsv); + const basicShape = { + name: Yup.string().required(t_i18n('This field is required')), + description: Yup.string().nullable(), + uri: Yup.string().required(t_i18n('This field is required')), + authentication_type: Yup.string().required(t_i18n('This field is required')), + authentication_value: Yup.string().nullable(), + current_state_date: Yup.date() + .typeError(t_i18n('The value must be a datetime (yyyy-MM-dd hh:mm (a|p)m)')) + .nullable(), + user_id: Yup.mixed().nullable(), + username: Yup.string().nullable(), + password: Yup.string().nullable(), + cert: Yup.string().nullable(), + key: Yup.string().nullable(), + ca: Yup.string().nullable(), + csv_mapper_id: Yup.mixed().required(t_i18n('This field is required')), + }; + + const ingestionCsvValidator = useSchemaEditionValidation('IngestionCsv', basicShape); + const [commitUpdate] = useMutation(ingestionCsvEditionPatch); + + const onSubmit: FormikConfig['onSubmit'] = (values, { setSubmitting }) => { + const { message, references, ...otherValues } = values; + const commitMessage = message ?? ''; + const commitReferences = (references ?? []).map(({ value }) => value); + const inputValues = Object.entries({ + ...otherValues, + }).map(([key, value]) => ({ key, value: adaptFieldValue(value) })); + commitUpdate({ + variables: { + id: ingestionCsvData.id, + input: inputValues, + commitMessage: commitMessage && commitMessage.length > 0 ? commitMessage : null, + references: commitReferences, + }, + onCompleted: () => { + setSubmitting(false); + handleClose(); + }, + }); + }; + + const handleSubmitField = (name: string, value: Option | string | string[] | number | number[] | null) => { + let finalValue = value as string; + if (name === 'csv_mapper_id' || name === 'user_id') { + finalValue = (value as Option).value; + } + ingestionCsvValidator + .validateAt(name, { [name]: value }) + .then(() => { + commitUpdate({ + variables: { + id: ingestionCsvData.id, + input: { key: name, value: finalValue || '' }, + }, + }); + }) + .catch(() => false); + }; + const initialValues = { + name: ingestionCsvData.name, + description: ingestionCsvData.description, + uri: ingestionCsvData.uri, + authentication_type: ingestionCsvData.authentication_type, + authentication_value: ingestionCsvData.authentication_value, + current_state_date: ingestionCsvData.current_state_date, + ingestion_running: ingestionCsvData.ingestion_running, + csv_mapper_id: convertMapper(ingestionCsvData, 'csvMapper'), + user_id: convertUser(ingestionCsvData, 'user'), + references: undefined, + }; + + const queryRef = useQueryLoading(csvMapperQuery); + + return ( + + enableReinitialize={true} + initialValues={initialValues} + validationSchema={ingestionCsvValidator} + onSubmit={onSubmit} + > + {({ + values, + submitForm, + isSubmitting, + setFieldValue, + isValid, + dirty, + }) => ( +
    + + + + + { + queryRef && ( + }> + option.value === value} + onChange={handleSubmitField} + queryRef={queryRef} + /> + + ) + } + + {t_i18n('None')} + {t_i18n('Basic user / password')} + {t_i18n('Bearer token')} + + {t_i18n('Client certificate')} + + + {values.authentication_type === 'basic' && ( + <> + + + + )} + {values.authentication_type === 'bearer' && ( + + )} + {values.authentication_type === 'certificate' && ( + <> + + + + + )} + option.value === value} + onChange={handleSubmitField} + containerStyle={fieldSpacingContainerStyle} + /> + {enableReferences && ( + + )} + + + {t_i18n('Please, verify the validity of the selected CSV mapper for the given URL.')}
    + {t_i18n('Only successful tests allow the ingestion edition.')} +
    +
    +
    + +
    + setOpen(false)} + uri={values.uri} + csvMapperId={values.csv_mapper_id} + /> + + )} + + ); +}; + +export default IngestionCsvEdition; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEditionContainer.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEditionContainer.tsx new file mode 100644 index 0000000000000..3ea2c30e4ecb0 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvEditionContainer.tsx @@ -0,0 +1,54 @@ +import { graphql, PreloadedQuery, usePreloadedQuery } from 'react-relay'; +import React, { FunctionComponent } from 'react'; +import Drawer, { DrawerVariant } from '@components/common/drawer/Drawer'; +import IngestionCsvEdition from '@components/data/ingestionCsv/IngestionCsvEdition'; +import { IngestionCsvEditionContainerQuery } from '@components/data/ingestionCsv/__generated__/IngestionCsvEditionContainerQuery.graphql'; +import { useFormatter } from '../../../../components/i18n'; +import Loader, { LoaderVariant } from '../../../../components/Loader'; +import { useIsEnforceReference } from '../../../../utils/hooks/useEntitySettings'; + +export const ingestionCsvEditionContainerQuery = graphql` + query IngestionCsvEditionContainerQuery($id: String!) { + ingestionCsv(id: $id) { + ...IngestionCsvEditionFragment_ingestionCsv + } + } +`; + +interface IngestionCsvEditionContainerProps { + queryRef: PreloadedQuery; + open: boolean; + handleClose?: () => void; +} + +const IngestionCsvEditionContainer: FunctionComponent = ({ + queryRef, + open, + handleClose, +}) => { + const { t_i18n } = useFormatter(); + + const { ingestionCsv } = usePreloadedQuery(ingestionCsvEditionContainerQuery, queryRef); + + if (!ingestionCsv) { + return ; + } + return ( + + {({ onClose }) => ( + + )} + + ); +}; + +export default IngestionCsvEditionContainer; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLine.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLine.tsx new file mode 100644 index 0000000000000..78b65ac3590dd --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLine.tsx @@ -0,0 +1,188 @@ +import makeStyles from '@mui/styles/makeStyles'; +import { graphql, useFragment } from 'react-relay'; +import React, { FunctionComponent } from 'react'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; +import Skeleton from '@mui/material/Skeleton'; +import { MoreVert } from '@mui/icons-material'; +import IngestionCsvPopover from '@components/data/ingestionCsv/IngestionCsvPopover'; +import { IngestionCsvLinesPaginationQuery$variables } from '@components/data/ingestionCsv/__generated__/IngestionCsvLinesPaginationQuery.graphql'; +import { IngestionCsvLine_node$key } from '@components/data/ingestionCsv/__generated__/IngestionCsvLine_node.graphql'; +import TableViewIcon from '@mui/icons-material/TableView'; +import ItemBoolean from '../../../../components/ItemBoolean'; +import { useFormatter } from '../../../../components/i18n'; +import { DataColumns } from '../../../../components/list_lines'; +import type { Theme } from '../../../../components/Theme'; + +const useStyles = makeStyles((theme) => ({ + item: { + paddingLeft: 10, + height: 50, + }, + itemIcon: { + color: theme.palette.primary.main, + }, + bodyItem: { + height: 20, + fontSize: 13, + float: 'left', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + paddingRight: 10, + }, + itemIconDisabled: { + color: theme.palette.grey?.[700], + }, +})); + +interface IngestionCsvLineProps { + node: IngestionCsvLine_node$key; + dataColumns: DataColumns; + onLabelClick: ( + k: string, + id: string, + value: Record, + event: React.KeyboardEvent, + ) => void; + paginationOptions?: IngestionCsvLinesPaginationQuery$variables; +} + +const ingestionCsvLineFragment = graphql` + fragment IngestionCsvLine_node on IngestionCsv { + id + name + uri + ingestion_running + current_state_date + } +`; + +export const IngestionCsvLineComponent: FunctionComponent = ({ + dataColumns, + node, + paginationOptions, +}) => { + const classes = useStyles(); + const { t_i18n, nsdt } = useFormatter(); + const data = useFragment(ingestionCsvLineFragment, node); + return ( + + + + + +
    + {data.name} +
    +
    + {data.uri} +
    +
    + +
    +
    + {nsdt(data.current_state_date)} +
    +
    + } + /> + + + + + ); +}; + +export const IngestionCsvLineDummy = ({ dataColumns }: { dataColumns: DataColumns }) => { + const classes = useStyles(); + return ( + + + + + +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + } + /> + + + + + ); +}; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLines.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLines.tsx new file mode 100644 index 0000000000000..643ad9757392c --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvLines.tsx @@ -0,0 +1,109 @@ +import { graphql, PreloadedQuery } from 'react-relay'; +import React, { FunctionComponent } from 'react'; +import { IngestionCsvLinesPaginationQuery, IngestionCsvLinesPaginationQuery$variables } from '@components/data/ingestionCsv/__generated__/IngestionCsvLinesPaginationQuery.graphql'; +import { IngestionCsvLines_data$key } from '@components/data/ingestionCsv/__generated__/IngestionCsvLines_data.graphql'; +import { IngestionCsvLineComponent, IngestionCsvLineDummy } from '@components/data/ingestionCsv/IngestionCsvLine'; +import { UseLocalStorageHelpers } from '../../../../utils/hooks/useLocalStorage'; +import { DataColumns } from '../../../../components/list_lines'; +import usePreloadedPaginationFragment from '../../../../utils/hooks/usePreloadedPaginationFragment'; +import ListLinesContent from '../../../../components/list_lines/ListLinesContent'; + +const nbOfRowsToLoad = 50; + +interface IngestionCsvLinesProps { + queryRef: PreloadedQuery; + dataColumns: DataColumns; + paginationOptions?: IngestionCsvLinesPaginationQuery$variables; + setNumberOfElements: UseLocalStorageHelpers['handleSetNumberOfElements']; +} + +export const ingestionCsvLinesQuery = graphql` + query IngestionCsvLinesPaginationQuery( + $search: String + $count: Int + $cursor: ID + $orderBy: IngestionCsvOrdering + $orderMode: OrderingMode + $filters: FilterGroup + ) { + ...IngestionCsvLines_data + @arguments( + search: $search + count: $count + cursor: $cursor + orderBy: $orderBy + orderMode: $orderMode + filters: $filters + ) + } +`; + +const ingestionCsvLinesFragment = graphql` + fragment IngestionCsvLines_data on Query + @argumentDefinitions( + search: { type: "String" } + count: { type: "Int", defaultValue: 25 } + cursor: { type: "ID" } + orderBy: { type: "IngestionCsvOrdering", defaultValue: name } + orderMode: { type: "OrderingMode", defaultValue: asc } + filters:{ type: "FilterGroup" } + ) + @refetchable(queryName: "IngestionCsvLinesRefetchQuery") { + ingestionCsvs( + search: $search + first: $count + after: $cursor + orderBy: $orderBy + orderMode: $orderMode + filters: $filters + ) @connection(key: "Pagination_ingestionCsvs") { + edges { + node { + id + ...IngestionCsvLine_node + } + } + pageInfo { + endCursor + hasNextPage + globalCount + } + } + } +`; + +const IngestionCsvLines: FunctionComponent = ({ + setNumberOfElements, + queryRef, + dataColumns, + paginationOptions, +}) => { + const { data, hasMore, loadMore, isLoadingMore } = usePreloadedPaginationFragment< + IngestionCsvLinesPaginationQuery, + IngestionCsvLines_data$key>({ + queryRef, + linesQuery: ingestionCsvLinesQuery, + linesFragment: ingestionCsvLinesFragment, + nodePath: ['ingestionCsvs', 'pageInfo', 'globalCount'], + setNumberOfElements, + }); + const ingestionCsvs = data?.ingestionCsvs?.edges ?? []; + const globalCount = data?.ingestionCsvs?.pageInfo?.globalCount; + return ( + + ); +}; + +export default IngestionCsvLines; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvMapperTestDialog.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvMapperTestDialog.tsx new file mode 100644 index 0000000000000..50e08ffb995f8 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvMapperTestDialog.tsx @@ -0,0 +1,154 @@ +import { graphql } from 'react-relay'; +import React, { FunctionComponent, useState } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import CodeBlock from '@components/common/CodeBlock'; +import { IngestionCsvMapperTestDialogQuery$data } from '@components/data/ingestionCsv/__generated__/IngestionCsvMapperTestDialogQuery.graphql'; +import { Option } from '@components/common/form/ReferenceField'; +import TextField from '@mui/material/TextField'; +import Alert from '@mui/material/Alert'; +import Loader, { LoaderVariant } from '../../../../components/Loader'; +import { useFormatter } from '../../../../components/i18n'; +import { fetchQuery, handleError } from '../../../../relay/environment'; + +const ingestionCsvMapperTestQuery = graphql` + query IngestionCsvMapperTestDialogQuery($uri: String!, $csv_mapper_id: String!) { + test_mapper(uri: $uri, csv_mapper_id: $csv_mapper_id) { + nbEntities + nbRelationships + objects + } + } +`; + +interface IngestionCsvMapperTestDialogProps { + open: boolean + onClose: () => void + uri: string + csvMapperId: string | Option + setIsCreateDisabled?: React.Dispatch> +} + +const IngestionCsvMapperTestDialog: FunctionComponent = ({ + open, + onClose, + uri, + csvMapperId, + setIsCreateDisabled, +}) => { + const { t_i18n } = useFormatter(); + const [result, setResult] = useState(undefined); + const [loading, setLoading] = useState(false); + + const handleClose = () => { + setResult(undefined); + onClose(); + }; + + const onTest = (url: string, csv_mapper_id: string) => { + setLoading(true); + fetchQuery(ingestionCsvMapperTestQuery, { uri: url, csv_mapper_id }) + .toPromise() + .then((data) => { + const resultTest = (data as IngestionCsvMapperTestDialogQuery$data) + .test_mapper; + if (resultTest) { + setResult({ + test_mapper: { + ...resultTest, + }, + }); + if (setIsCreateDisabled) { + setIsCreateDisabled(resultTest.nbEntities === 0); + } + } + setLoading(false); + }).catch((error) => { + handleError(error); + setLoading(false); + }); + }; + + return ( + + {t_i18n('Testing csv mapper')} + + + + + + +
    + + {t_i18n('Please, note that the test will be run on the 50 first lines')} + +
    +
    + + + {loading && ( + + + + )} + {result + && + {t_i18n('Objects found')} : + {result?.test_mapper?.nbEntities} {t_i18n('Entities')} + {result?.test_mapper?.nbRelationships} {t_i18n('Relationships')} + + } + + + + +
    +
    + ); +}; + +export default IngestionCsvMapperTestDialog; diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvPopover.tsx b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvPopover.tsx new file mode 100644 index 0000000000000..586ddcd954873 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionCsv/IngestionCsvPopover.tsx @@ -0,0 +1,265 @@ +import { graphql, useMutation } from 'react-relay'; +import React, { FunctionComponent, useState } from 'react'; +import { PopoverProps } from '@mui/material/Popover'; +import IconButton from '@mui/material/IconButton'; +import MoreVert from '@mui/icons-material/MoreVert'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import { Button } from '@mui/material'; +import DialogActions from '@mui/material/DialogActions'; +import IngestionCsvEditionContainer, { ingestionCsvEditionContainerQuery } from '@components/data/ingestionCsv/IngestionCsvEditionContainer'; +import { ingestionCsvEditionPatch } from '@components/data/ingestionCsv/IngestionCsvEdition'; +import { IngestionCsvLinesPaginationQuery$variables } from '@components/data/ingestionCsv/__generated__/IngestionCsvLinesPaginationQuery.graphql'; +import { IngestionCsvEditionContainerQuery } from '@components/data/ingestionCsv/__generated__/IngestionCsvEditionContainerQuery.graphql'; +import Loader, { LoaderVariant } from '../../../../components/Loader'; +import { deleteNode } from '../../../../utils/store'; +import useQueryLoading from '../../../../utils/hooks/useQueryLoading'; +import { useFormatter } from '../../../../components/i18n'; +import Transition from '../../../../components/Transition'; + +const ingestionCsvPopoverDeletionMutation = graphql` + mutation IngestionCsvPopoverDeletionMutation($id: ID!) { + ingestionCsvDelete(id: $id) + } +`; + +interface IngestionCsvPopoverProps { + ingestionCsvId: string; + running?: boolean | null; + paginationOptions?: IngestionCsvLinesPaginationQuery$variables; +} + +const IngestionCsvPopover: FunctionComponent = ({ + ingestionCsvId, + paginationOptions, + running, +}) => { + const { t_i18n } = useFormatter(); + const [anchorEl, setAnchorEl] = useState(null); + const [displayStart, setDisplayStart] = useState(false); + const [starting, setStarting] = useState(false); + const [displayStop, setDisplayStop] = useState(false); + const [stopping, setStopping] = useState(false); + const handleOpen = (event: React.MouseEvent) => setAnchorEl(event.currentTarget); + const handleClose = () => setAnchorEl(null); + + // -- Edition -- + const [displayUpdate, setDisplayUpdate] = useState(false); + const handleOpenUpdate = () => { + setDisplayUpdate(true); + handleClose(); + }; + const handleOpenStart = () => { + setDisplayStart(true); + handleClose(); + }; + + const handleCloseStart = () => { + setDisplayStart(false); + }; + + const handleOpenStop = () => { + setDisplayStop(true); + handleClose(); + }; + + const handleCloseStop = () => { + setDisplayStop(false); + }; + const queryRef = useQueryLoading( + ingestionCsvEditionContainerQuery, + { id: ingestionCsvId }, + ); + + // -- Deletion -- + const [displayDelete, setDisplayDelete] = useState(false); + const [deleting, setDeleting] = useState(false); + const [commit] = useMutation(ingestionCsvPopoverDeletionMutation); + const handleOpenDelete = () => { + setDisplayDelete(true); + handleClose(); + }; + + const handleCloseDelete = () => { + setDisplayDelete(false); + }; + const submitDelete = () => { + setDeleting(true); + commit({ + variables: { + id: ingestionCsvId, + }, + updater: (store) => { + deleteNode(store, 'Pagination_ingestionCsvs', paginationOptions, ingestionCsvId); + }, + onCompleted: () => { + setDeleting(false); + handleCloseDelete(); + }, + }); + }; + + // -- Running -- + const [commitRunning] = useMutation(ingestionCsvEditionPatch); + const submitStart = () => { + setStarting(true); + commitRunning({ + variables: { + id: ingestionCsvId, + input: { key: 'ingestion_running', value: ['true'] }, + }, + onCompleted: () => { + setStarting(false); + handleCloseStart(); + }, + }); + }; + + const submitStop = () => { + setStopping(true); + commitRunning({ + variables: { + id: ingestionCsvId, + input: { key: 'ingestion_running', value: ['false'] }, + }, + onCompleted: () => { + setStopping(false); + handleCloseStop(); + }, + }); + }; + return ( + <> +
    + + + + + {!running && ( + + {t_i18n('Start')} + + )} + {running && ( + + {t_i18n('Stop')} + + )} + + {t_i18n('Update')} + + + {t_i18n('Delete')} + + + {queryRef && ( + }> + setDisplayUpdate(false)} + open={displayUpdate} + /> + + )} + + + + {t_i18n('Do you want to delete this CSV ingester?')} + + + + + + + + + + + {t_i18n('Do you want to start this CSV ingester?')} + + + + + + + + + + + {t_i18n('Do you want to stop this CSV ingester?')} + + + + + + + +
    + + ); +}; + +export default IngestionCsvPopover; diff --git a/opencti-platform/opencti-front/src/schema/relay.schema.graphql b/opencti-platform/opencti-front/src/schema/relay.schema.graphql index 8176b0722f3b7..b1b8aa746f182 100644 --- a/opencti-platform/opencti-front/src/schema/relay.schema.graphql +++ b/opencti-platform/opencti-front/src/schema/relay.schema.graphql @@ -7315,6 +7315,9 @@ type Query { ingestionRsss(first: Int, after: ID, orderBy: IngestionRssOrdering, orderMode: OrderingMode, filters: FilterGroup, includeAuthorities: Boolean, search: String): IngestionRssConnection ingestionTaxii(id: String!): IngestionTaxii ingestionTaxiis(first: Int, after: ID, orderBy: IngestionTaxiiOrdering, orderMode: OrderingMode, filters: FilterGroup, includeAuthorities: Boolean, search: String): IngestionTaxiiConnection + ingestionCsv(id: String!): IngestionCsv + ingestionCsvs(first: Int, after: ID, orderBy: IngestionCsvOrdering, orderMode: OrderingMode, filters: FilterGroup, includeAuthorities: Boolean, search: String): IngestionCsvConnection + test_mapper(uri: String!, csv_mapper_id: String!): CsvMapperTestResult indicator(id: String!): Indicator indicators(first: Int, after: ID, orderBy: IndicatorsOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String): IndicatorConnection indicatorsTimeSeries(objectId: String, field: String!, operation: StatsOperation!, startDate: DateTime!, endDate: DateTime!, interval: String!, filters: FilterGroup): [TimeSeries] @@ -8049,6 +8052,9 @@ type Mutation { ingestionTaxiiAdd(input: IngestionTaxiiAddInput!): IngestionTaxii ingestionTaxiiDelete(id: ID!): ID ingestionTaxiiFieldPatch(id: ID!, input: [EditInput!]!): IngestionTaxii + ingestionCsvAdd(input: IngestionCsvAddInput!): IngestionCsv + ingestionCsvDelete(id: ID!): ID + ingestionCsvFieldPatch(id: ID!, input: [EditInput!]!): IngestionCsv indicatorAdd(input: IndicatorAddInput!): Indicator indicatorDelete(id: ID!): ID indicatorFieldPatch(id: ID!, input: [EditInput]!, commitMessage: String, references: [String]): Indicator @@ -10524,6 +10530,63 @@ input IngestionTaxiiAddInput { scalar collection_String_NotNull_minLength_5 +enum CsvAuthType { + none + basic + bearer + certificate +} + +type IngestionCsv implements InternalObject & BasicObject { + id: ID! + entity_type: String! + standard_id: String! + parent_types: [String]! + created_at: DateTime + updated_at: DateTime + name: String! + description: String + uri: String! + csvMapper: CsvMapper! + authentication_type: CsvAuthType! + authentication_value: String + user_id: String! + user: Creator + ingestion_running: Boolean + current_state_hash: String + current_state_date: DateTime +} + +enum IngestionCsvOrdering { + name + created_at + updated_at + uri + mapper +} + +type IngestionCsvConnection { + pageInfo: PageInfo! + edges: [IngestionCsvEdge!]! +} + +type IngestionCsvEdge { + cursor: String! + node: IngestionCsv! +} + +input IngestionCsvAddInput { + name: name_String_NotNull_minLength_2! + description: String + authentication_type: CsvAuthType! + authentication_value: String + current_state_date: DateTime + uri: uri_String_NotNull_minLength_5! + csv_mapper_id: String! + ingestion_running: Boolean + user_id: String! +} + enum IndicatorsOrdering { pattern_type pattern_version diff --git a/opencti-platform/opencti-front/src/utils/Localization.js b/opencti-platform/opencti-front/src/utils/Localization.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/opencti-platform/opencti-front/src/utils/edition.js b/opencti-platform/opencti-front/src/utils/edition.js index c287fe6babc25..40dd0a4735361 100644 --- a/opencti-platform/opencti-front/src/utils/edition.js +++ b/opencti-platform/opencti-front/src/utils/edition.js @@ -92,6 +92,15 @@ export const convertUser = (element, field = 'user') => (isEmptyField(element?.[ type: element[field].entity_type, }); +export const convertMapper = (element, field = 'csvMapper') => { + return (isEmptyField(element?.[field]) + ? '' + : { + label: element[field].name, + value: element[field].id, + }); +}; + export const convertNotifiers = (element) => element?.notifiers?.map(({ id, name }) => ({ value: id, label: name })); export const filterEventTypesOptions = [ diff --git a/opencti-platform/opencti-graphql/graphql-codegen.yml b/opencti-platform/opencti-graphql/graphql-codegen.yml index 10439c9628f58..b31d7d66c127a 100644 --- a/opencti-platform/opencti-graphql/graphql-codegen.yml +++ b/opencti-platform/opencti-graphql/graphql-codegen.yml @@ -43,6 +43,7 @@ generates: IngestionRss: ../modules/ingestion/ingestion-types#BasicStoreEntityIngestionRss IngestionTaxii: ../modules/ingestion/ingestion-types#BasicStoreEntityIngestionTaxii Indicator: ../modules/indicator/indicator-types#BasicStoreEntityIndicator + IngestionCsv: ../modules/ingestion/ingestion-types#BasicStoreEntityIngestionCsv Organization: ../modules/organization/organization-types#BasicStoreEntityOrganization CsvMapper: ../modules/internal/csvMapper/csvMapper-types#BasicStoreEntityCsvMapper Playbook: ../modules/playbook/playbook-types#BasicStoreEntityPlaybook diff --git a/opencti-platform/opencti-graphql/src/domain/work.js b/opencti-platform/opencti-graphql/src/domain/work.js index 89853710d676b..38d7d084ac690 100644 --- a/opencti-platform/opencti-graphql/src/domain/work.js +++ b/opencti-platform/opencti-graphql/src/domain/work.js @@ -218,7 +218,7 @@ export const reportExpectation = async (context, user, workId, errorData) => { } // Update elastic const currentWork = await loadWorkById(context, user, workId); - await elUpdate(currentWork._index, workId, { script: { source: sourceScript, lang: 'painless', params } }); + await elUpdate(currentWork?._index, workId, { script: { source: sourceScript, lang: 'painless', params } }); } return workId; }; diff --git a/opencti-platform/opencti-graphql/src/generated/graphql.ts b/opencti-platform/opencti-graphql/src/generated/graphql.ts index 7e2642d0d4e5a..1fdd31fd96ae4 100644 --- a/opencti-platform/opencti-graphql/src/generated/graphql.ts +++ b/opencti-platform/opencti-graphql/src/generated/graphql.ts @@ -23,7 +23,7 @@ import type { BasicStoreEntityMalwareAnalysis } from '../modules/malwareAnalysis import type { BasicStoreEntityManagerConfiguration } from '../modules/managerConfiguration/managerConfiguration-types'; import type { BasicStoreEntityNotifier } from '../modules/notifier/notifier-types'; import type { BasicStoreEntityThreatActorIndividual } from '../modules/threatActorIndividual/threatActorIndividual-types'; -import type { BasicStoreEntityIngestionRss, BasicStoreEntityIngestionTaxii } from '../modules/ingestion/ingestion-types'; +import type { BasicStoreEntityIngestionRss, BasicStoreEntityIngestionTaxii, BasicStoreEntityIngestionCsv } from '../modules/ingestion/ingestion-types'; import type { BasicStoreEntityIndicator } from '../modules/indicator/indicator-types'; import type { BasicStoreEntityOrganization } from '../modules/organization/organization-types'; import type { BasicStoreEntityCsvMapper } from '../modules/internal/csvMapper/csvMapper-types'; @@ -4593,6 +4593,13 @@ export type CryptographicKeyAddInput = { value?: InputMaybe; }; +export enum CsvAuthType { + Basic = 'basic', + Bearer = 'bearer', + Certificate = 'certificate', + None = 'none' +} + export type CsvMapper = BasicObject & InternalObject & { __typename?: 'CsvMapper'; entity_type: Scalars['String']['output']; @@ -9680,6 +9687,59 @@ export enum InfrastructuresOrdering { XOpenctiWorkflowId = 'x_opencti_workflow_id' } +export type IngestionCsv = BasicObject & InternalObject & { + __typename?: 'IngestionCsv'; + authentication_type: CsvAuthType; + authentication_value?: Maybe; + created_at?: Maybe; + csvMapper: CsvMapper; + current_state_date?: Maybe; + current_state_hash?: Maybe; + description?: Maybe; + entity_type: Scalars['String']['output']; + id: Scalars['ID']['output']; + ingestion_running?: Maybe; + name: Scalars['String']['output']; + parent_types: Array>; + standard_id: Scalars['String']['output']; + updated_at?: Maybe; + uri: Scalars['String']['output']; + user?: Maybe; + user_id: Scalars['String']['output']; +}; + +export type IngestionCsvAddInput = { + authentication_type: CsvAuthType; + authentication_value?: InputMaybe; + csv_mapper_id: Scalars['String']['input']; + current_state_date?: InputMaybe; + description?: InputMaybe; + ingestion_running?: InputMaybe; + name: Scalars['String']['input']; + uri: Scalars['String']['input']; + user_id: Scalars['String']['input']; +}; + +export type IngestionCsvConnection = { + __typename?: 'IngestionCsvConnection'; + edges: Array; + pageInfo: PageInfo; +}; + +export type IngestionCsvEdge = { + __typename?: 'IngestionCsvEdge'; + cursor: Scalars['String']['output']; + node: IngestionCsv; +}; + +export enum IngestionCsvOrdering { + CreatedAt = 'created_at', + Mapper = 'mapper', + Name = 'name', + UpdatedAt = 'updated_at', + Uri = 'uri' +} + export type IngestionRss = BasicObject & InternalObject & { __typename?: 'IngestionRss'; created_at?: Maybe; @@ -12054,6 +12114,9 @@ export type Mutation = { individualEdit?: Maybe; infrastructureAdd?: Maybe; infrastructureEdit?: Maybe; + ingestionCsvAdd?: Maybe; + ingestionCsvDelete?: Maybe; + ingestionCsvFieldPatch?: Maybe; ingestionRssAdd?: Maybe; ingestionRssDelete?: Maybe; ingestionRssFieldPatch?: Maybe; @@ -12846,6 +12909,22 @@ export type MutationInfrastructureEditArgs = { }; +export type MutationIngestionCsvAddArgs = { + input: IngestionCsvAddInput; +}; + + +export type MutationIngestionCsvDeleteArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationIngestionCsvFieldPatchArgs = { + id: Scalars['ID']['input']; + input: Array; +}; + + export type MutationIngestionRssAddArgs = { input: IngestionRssAddInput; }; @@ -17077,6 +17156,8 @@ export type Query = { individuals?: Maybe; infrastructure?: Maybe; infrastructures?: Maybe; + ingestionCsv?: Maybe; + ingestionCsvs?: Maybe; ingestionRss?: Maybe; ingestionRsss?: Maybe; ingestionTaxii?: Maybe; @@ -17238,6 +17319,7 @@ export type Query = { tasks?: Maybe; taxiiCollection?: Maybe; taxiiCollections?: Maybe; + test_mapper?: Maybe; threatActor?: Maybe; threatActorGroup?: Maybe; threatActorIndividual?: Maybe; @@ -17984,6 +18066,22 @@ export type QueryInfrastructuresArgs = { }; +export type QueryIngestionCsvArgs = { + id: Scalars['String']['input']; +}; + + +export type QueryIngestionCsvsArgs = { + after?: InputMaybe; + filters?: InputMaybe; + first?: InputMaybe; + includeAuthorities?: InputMaybe; + orderBy?: InputMaybe; + orderMode?: InputMaybe; + search?: InputMaybe; +}; + + export type QueryIngestionRssArgs = { id: Scalars['String']['input']; }; @@ -19368,6 +19466,12 @@ export type QueryTaxiiCollectionsArgs = { }; +export type QueryTest_MapperArgs = { + csv_mapper_id: Scalars['String']['input']; + uri: Scalars['String']['input']; +}; + + export type QueryThreatActorArgs = { id?: InputMaybe; }; @@ -27237,13 +27341,13 @@ export type ResolversUnionTypes> = Resol /** Mapping of interface types */ export type ResolversInterfaceTypes> = ResolversObject<{ BackgroundTask: ( ListTask ) | ( QueryTask ) | ( RuleTask ); - BasicObject: ( BasicStoreEntityAdministrativeArea ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, coursesOfAction?: Maybe, createdBy?: Maybe, dataComponents?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentAttackPatterns?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subAttackPatterns?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Capability ) | ( BasicStoreEntityCaseIncident ) | ( BasicStoreEntityCaseRfi ) | ( BasicStoreEntityCaseRft ) | ( BasicStoreEntityCaseTemplate ) | ( BasicStoreEntityChannel ) | ( Omit & { administrativeArea?: Maybe, cases?: Maybe, containers?: Maybe, country?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Connector ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, region?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { attackPatterns?: Maybe, cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityCsvMapper ) | ( BasicStoreEntityDataComponent ) | ( BasicStoreEntityDataSource ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityEntitySetting ) | ( BasicStoreEntityEvent ) | ( ExternalReference ) | ( BasicStoreEntityFeedback ) | ( Omit & { default_dashboard?: Maybe, members?: Maybe } ) | ( BasicStoreEntityGrouping ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityIndicator ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityIngestionRss ) | ( BasicStoreEntityIngestionTaxii ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( KillChainPhase ) | ( Label ) | ( BasicStoreEntityLanguage ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityMalwareAnalysis ) | ( BasicStoreEntityManagerConfiguration ) | ( MarkingDefinition ) | ( Omit & { administrated_organizations: Array, default_dashboard?: Maybe, default_dashboards: Array, groups?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityNarrative ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityNotification ) | ( BasicStoreEntityNotifier ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityOrganization ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityPlaybook ) | ( Omit & { cases?: Maybe, city?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, serviceDlls?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentRegions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subRegions?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Role ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentSectors?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subSectors?: Maybe, targetedOrganizations?: Maybe } ) | ( Omit & { platform_critical_alerts: Array, platform_organization?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, vulnerabilities?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, obsContent?: Maybe, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTask ) | ( TaskTemplate ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityThreatActorIndividual ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTrigger ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { administrated_organizations: Array, groups?: Maybe, objectOrganization?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityVocabulary ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, softwares?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityWorkspace ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); + BasicObject: ( BasicStoreEntityAdministrativeArea ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, coursesOfAction?: Maybe, createdBy?: Maybe, dataComponents?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentAttackPatterns?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subAttackPatterns?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Capability ) | ( BasicStoreEntityCaseIncident ) | ( BasicStoreEntityCaseRfi ) | ( BasicStoreEntityCaseRft ) | ( BasicStoreEntityCaseTemplate ) | ( BasicStoreEntityChannel ) | ( Omit & { administrativeArea?: Maybe, cases?: Maybe, containers?: Maybe, country?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Connector ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, region?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { attackPatterns?: Maybe, cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityCsvMapper ) | ( BasicStoreEntityDataComponent ) | ( BasicStoreEntityDataSource ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityEntitySetting ) | ( BasicStoreEntityEvent ) | ( ExternalReference ) | ( BasicStoreEntityFeedback ) | ( Omit & { default_dashboard?: Maybe, members?: Maybe } ) | ( BasicStoreEntityGrouping ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityIndicator ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityIngestionCsv ) | ( BasicStoreEntityIngestionRss ) | ( BasicStoreEntityIngestionTaxii ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( KillChainPhase ) | ( Label ) | ( BasicStoreEntityLanguage ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityMalwareAnalysis ) | ( BasicStoreEntityManagerConfiguration ) | ( MarkingDefinition ) | ( Omit & { administrated_organizations: Array, default_dashboard?: Maybe, default_dashboards: Array, groups?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityNarrative ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityNotification ) | ( BasicStoreEntityNotifier ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityOrganization ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityPlaybook ) | ( Omit & { cases?: Maybe, city?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, serviceDlls?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentRegions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subRegions?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Role ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentSectors?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subSectors?: Maybe, targetedOrganizations?: Maybe } ) | ( Omit & { platform_critical_alerts: Array, platform_organization?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, vulnerabilities?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, obsContent?: Maybe, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTask ) | ( TaskTemplate ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityThreatActorIndividual ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTrigger ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { administrated_organizations: Array, groups?: Maybe, objectOrganization?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityVocabulary ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, softwares?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityWorkspace ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); BasicRelationship: ( InternalRelationship ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, from?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, to?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, from?: Maybe, groupings?: Maybe, notes?: Maybe, opinions?: Maybe, reports?: Maybe, to?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, from?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, opinions?: Maybe, reports?: Maybe, to?: Maybe } ); Case: ( BasicStoreEntityCaseIncident ) | ( BasicStoreEntityCaseRfi ) | ( BasicStoreEntityCaseRft ) | ( BasicStoreEntityFeedback ); Container: ( BasicStoreEntityCaseIncident ) | ( BasicStoreEntityCaseRfi ) | ( BasicStoreEntityCaseRft ) | ( BasicStoreEntityFeedback ) | ( BasicStoreEntityGrouping ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTask ); HashedObservable: ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, obsContent?: Maybe, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); Identity: ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityOrganization ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentSectors?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subSectors?: Maybe, targetedOrganizations?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); - InternalObject: ( Capability ) | ( BasicStoreEntityCaseTemplate ) | ( Connector ) | ( BasicStoreEntityCsvMapper ) | ( BasicStoreEntityEntitySetting ) | ( Omit & { default_dashboard?: Maybe, members?: Maybe } ) | ( BasicStoreEntityIngestionRss ) | ( BasicStoreEntityIngestionTaxii ) | ( BasicStoreEntityManagerConfiguration ) | ( Omit & { administrated_organizations: Array, default_dashboard?: Maybe, default_dashboards: Array, groups?: Maybe } ) | ( BasicStoreEntityNotification ) | ( BasicStoreEntityNotifier ) | ( BasicStoreEntityPlaybook ) | ( Role ) | ( Omit & { platform_critical_alerts: Array, platform_organization?: Maybe } ) | ( TaskTemplate ) | ( BasicStoreEntityTrigger ) | ( Omit & { administrated_organizations: Array, groups?: Maybe, objectOrganization?: Maybe } ) | ( BasicStoreEntityWorkspace ); + InternalObject: ( Capability ) | ( BasicStoreEntityCaseTemplate ) | ( Connector ) | ( BasicStoreEntityCsvMapper ) | ( BasicStoreEntityEntitySetting ) | ( Omit & { default_dashboard?: Maybe, members?: Maybe } ) | ( BasicStoreEntityIngestionCsv ) | ( BasicStoreEntityIngestionRss ) | ( BasicStoreEntityIngestionTaxii ) | ( BasicStoreEntityManagerConfiguration ) | ( Omit & { administrated_organizations: Array, default_dashboard?: Maybe, default_dashboards: Array, groups?: Maybe } ) | ( BasicStoreEntityNotification ) | ( BasicStoreEntityNotifier ) | ( BasicStoreEntityPlaybook ) | ( Role ) | ( Omit & { platform_critical_alerts: Array, platform_organization?: Maybe } ) | ( TaskTemplate ) | ( BasicStoreEntityTrigger ) | ( Omit & { administrated_organizations: Array, groups?: Maybe, objectOrganization?: Maybe } ) | ( BasicStoreEntityWorkspace ); Location: ( BasicStoreEntityAdministrativeArea ) | ( Omit & { administrativeArea?: Maybe, cases?: Maybe, containers?: Maybe, country?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, region?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, city?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentRegions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subRegions?: Maybe } ); StixCoreObject: ( BasicStoreEntityAdministrativeArea ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, coursesOfAction?: Maybe, createdBy?: Maybe, dataComponents?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentAttackPatterns?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subAttackPatterns?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityCaseIncident ) | ( BasicStoreEntityCaseRfi ) | ( BasicStoreEntityCaseRft ) | ( BasicStoreEntityChannel ) | ( Omit & { administrativeArea?: Maybe, cases?: Maybe, containers?: Maybe, country?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, region?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { attackPatterns?: Maybe, cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityDataComponent ) | ( BasicStoreEntityDataSource ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityEvent ) | ( BasicStoreEntityFeedback ) | ( BasicStoreEntityGrouping ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityIndicator ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityLanguage ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityMalwareAnalysis ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityNarrative ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityOrganization ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, city?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, serviceDlls?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentRegions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subRegions?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, relatedContainers?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, parentSectors?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, subSectors?: Maybe, targetedOrganizations?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, vulnerabilities?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, obsContent?: Maybe, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, organizations?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityTask ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, locations?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( BasicStoreEntityThreatActorIndividual ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, softwares?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); StixCyberObservable: ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, countries?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, serviceDlls?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe, vulnerabilities?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, obsContent?: Maybe, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ) | ( Omit & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe } ); @@ -27395,6 +27499,7 @@ export type ResolversTypes = ResolversObject<{ CryptocurrencyWalletAddInput: CryptocurrencyWalletAddInput; CryptographicKey: ResolverTypeWrapper & { cases?: Maybe, containers?: Maybe, createdBy?: Maybe, groupings?: Maybe, indicators?: Maybe, notes?: Maybe, objectOrganization?: Maybe>, observedData?: Maybe, opinions?: Maybe, reports?: Maybe, stixCoreRelationships?: Maybe }>; CryptographicKeyAddInput: CryptographicKeyAddInput; + CsvAuthType: CsvAuthType; CsvMapper: ResolverTypeWrapper; CsvMapperAddInput: CsvMapperAddInput; CsvMapperConnection: ResolverTypeWrapper & { edges: Array }>; @@ -27558,6 +27663,11 @@ export type ResolversTypes = ResolversObject<{ InfrastructureEdge: ResolverTypeWrapper & { node: ResolversTypes['Infrastructure'] }>; InfrastructureEditMutations: ResolverTypeWrapper & { contextClean?: Maybe, contextPatch?: Maybe, fieldPatch?: Maybe, relationAdd?: Maybe, relationDelete?: Maybe }>; InfrastructuresOrdering: InfrastructuresOrdering; + IngestionCsv: ResolverTypeWrapper; + IngestionCsvAddInput: IngestionCsvAddInput; + IngestionCsvConnection: ResolverTypeWrapper & { edges: Array }>; + IngestionCsvEdge: ResolverTypeWrapper & { node: ResolversTypes['IngestionCsv'] }>; + IngestionCsvOrdering: IngestionCsvOrdering; IngestionRss: ResolverTypeWrapper; IngestionRssAddInput: IngestionRssAddInput; IngestionRssConnection: ResolverTypeWrapper & { edges: Array }>; @@ -28272,6 +28382,10 @@ export type ResolversParentTypes = ResolversObject<{ InfrastructureConnection: Omit & { edges?: Maybe>> }; InfrastructureEdge: Omit & { node: ResolversParentTypes['Infrastructure'] }; InfrastructureEditMutations: Omit & { contextClean?: Maybe, contextPatch?: Maybe, fieldPatch?: Maybe, relationAdd?: Maybe, relationDelete?: Maybe }; + IngestionCsv: BasicStoreEntityIngestionCsv; + IngestionCsvAddInput: IngestionCsvAddInput; + IngestionCsvConnection: Omit & { edges: Array }; + IngestionCsvEdge: Omit & { node: ResolversParentTypes['IngestionCsv'] }; IngestionRss: BasicStoreEntityIngestionRss; IngestionRssAddInput: IngestionRssAddInput; IngestionRssConnection: Omit & { edges: Array }; @@ -29146,7 +29260,7 @@ export type BankAccountResolvers; export type BasicObjectResolvers = ResolversObject<{ - __resolveType: TypeResolveFn<'AdministrativeArea' | 'Artifact' | 'AttackPattern' | 'AutonomousSystem' | 'BankAccount' | 'Campaign' | 'Capability' | 'CaseIncident' | 'CaseRfi' | 'CaseRft' | 'CaseTemplate' | 'Channel' | 'City' | 'Connector' | 'Country' | 'CourseOfAction' | 'CryptocurrencyWallet' | 'CryptographicKey' | 'CsvMapper' | 'DataComponent' | 'DataSource' | 'Directory' | 'DomainName' | 'EmailAddr' | 'EmailMessage' | 'EmailMimePartType' | 'EntitySetting' | 'Event' | 'ExternalReference' | 'Feedback' | 'Group' | 'Grouping' | 'Hostname' | 'IPv4Addr' | 'IPv6Addr' | 'Incident' | 'Indicator' | 'Individual' | 'Infrastructure' | 'IngestionRss' | 'IngestionTaxii' | 'IntrusionSet' | 'KillChainPhase' | 'Label' | 'Language' | 'MacAddr' | 'Malware' | 'MalwareAnalysis' | 'ManagerConfiguration' | 'MarkingDefinition' | 'MeUser' | 'MediaContent' | 'Mutex' | 'Narrative' | 'NetworkTraffic' | 'Note' | 'Notification' | 'Notifier' | 'ObservedData' | 'Opinion' | 'Organization' | 'PaymentCard' | 'PhoneNumber' | 'Playbook' | 'Position' | 'Process' | 'Region' | 'Report' | 'Role' | 'Sector' | 'Settings' | 'Software' | 'StixFile' | 'System' | 'Task' | 'TaskTemplate' | 'Text' | 'ThreatActorGroup' | 'ThreatActorIndividual' | 'Tool' | 'Trigger' | 'Url' | 'User' | 'UserAccount' | 'UserAgent' | 'Vocabulary' | 'Vulnerability' | 'WindowsRegistryKey' | 'WindowsRegistryValueType' | 'Workspace' | 'X509Certificate', ParentType, ContextType>; + __resolveType: TypeResolveFn<'AdministrativeArea' | 'Artifact' | 'AttackPattern' | 'AutonomousSystem' | 'BankAccount' | 'Campaign' | 'Capability' | 'CaseIncident' | 'CaseRfi' | 'CaseRft' | 'CaseTemplate' | 'Channel' | 'City' | 'Connector' | 'Country' | 'CourseOfAction' | 'CryptocurrencyWallet' | 'CryptographicKey' | 'CsvMapper' | 'DataComponent' | 'DataSource' | 'Directory' | 'DomainName' | 'EmailAddr' | 'EmailMessage' | 'EmailMimePartType' | 'EntitySetting' | 'Event' | 'ExternalReference' | 'Feedback' | 'Group' | 'Grouping' | 'Hostname' | 'IPv4Addr' | 'IPv6Addr' | 'Incident' | 'Indicator' | 'Individual' | 'Infrastructure' | 'IngestionCsv' | 'IngestionRss' | 'IngestionTaxii' | 'IntrusionSet' | 'KillChainPhase' | 'Label' | 'Language' | 'MacAddr' | 'Malware' | 'MalwareAnalysis' | 'ManagerConfiguration' | 'MarkingDefinition' | 'MeUser' | 'MediaContent' | 'Mutex' | 'Narrative' | 'NetworkTraffic' | 'Note' | 'Notification' | 'Notifier' | 'ObservedData' | 'Opinion' | 'Organization' | 'PaymentCard' | 'PhoneNumber' | 'Playbook' | 'Position' | 'Process' | 'Region' | 'Report' | 'Role' | 'Sector' | 'Settings' | 'Software' | 'StixFile' | 'System' | 'Task' | 'TaskTemplate' | 'Text' | 'ThreatActorGroup' | 'ThreatActorIndividual' | 'Tool' | 'Trigger' | 'Url' | 'User' | 'UserAccount' | 'UserAgent' | 'Vocabulary' | 'Vulnerability' | 'WindowsRegistryKey' | 'WindowsRegistryValueType' | 'Workspace' | 'X509Certificate', ParentType, ContextType>; entity_type?: Resolver; id?: Resolver; parent_types?: Resolver>, ParentType, ContextType>; @@ -31812,6 +31926,39 @@ export type InfrastructureEditMutationsResolvers; }>; +export type IngestionCsvResolvers = ResolversObject<{ + authentication_type?: Resolver; + authentication_value?: Resolver, ParentType, ContextType>; + created_at?: Resolver, ParentType, ContextType>; + csvMapper?: Resolver; + current_state_date?: Resolver, ParentType, ContextType>; + current_state_hash?: Resolver, ParentType, ContextType>; + description?: Resolver, ParentType, ContextType>; + entity_type?: Resolver; + id?: Resolver; + ingestion_running?: Resolver, ParentType, ContextType>; + name?: Resolver; + parent_types?: Resolver>, ParentType, ContextType>; + standard_id?: Resolver; + updated_at?: Resolver, ParentType, ContextType>; + uri?: Resolver; + user?: Resolver, ParentType, ContextType>; + user_id?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}>; + +export type IngestionCsvConnectionResolvers = ResolversObject<{ + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}>; + +export type IngestionCsvEdgeResolvers = ResolversObject<{ + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}>; + export type IngestionRssResolvers = ResolversObject<{ created_at?: Resolver, ParentType, ContextType>; current_state_date?: Resolver, ParentType, ContextType>; @@ -31877,7 +32024,7 @@ export type IngestionTaxiiEdgeResolvers; export type InternalObjectResolvers = ResolversObject<{ - __resolveType: TypeResolveFn<'Capability' | 'CaseTemplate' | 'Connector' | 'CsvMapper' | 'EntitySetting' | 'Group' | 'IngestionRss' | 'IngestionTaxii' | 'ManagerConfiguration' | 'MeUser' | 'Notification' | 'Notifier' | 'Playbook' | 'Role' | 'Settings' | 'TaskTemplate' | 'Trigger' | 'User' | 'Workspace', ParentType, ContextType>; + __resolveType: TypeResolveFn<'Capability' | 'CaseTemplate' | 'Connector' | 'CsvMapper' | 'EntitySetting' | 'Group' | 'IngestionCsv' | 'IngestionRss' | 'IngestionTaxii' | 'ManagerConfiguration' | 'MeUser' | 'Notification' | 'Notifier' | 'Playbook' | 'Role' | 'Settings' | 'TaskTemplate' | 'Trigger' | 'User' | 'Workspace', ParentType, ContextType>; entity_type?: Resolver; id?: Resolver; }>; @@ -32802,6 +32949,9 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; infrastructureAdd?: Resolver, ParentType, ContextType, RequireFields>; infrastructureEdit?: Resolver, ParentType, ContextType, RequireFields>; + ingestionCsvAdd?: Resolver, ParentType, ContextType, RequireFields>; + ingestionCsvDelete?: Resolver, ParentType, ContextType, RequireFields>; + ingestionCsvFieldPatch?: Resolver, ParentType, ContextType, RequireFields>; ingestionRssAdd?: Resolver, ParentType, ContextType, RequireFields>; ingestionRssDelete?: Resolver, ParentType, ContextType, RequireFields>; ingestionRssFieldPatch?: Resolver, ParentType, ContextType, RequireFields>; @@ -34072,6 +34222,8 @@ export type QueryResolvers, ParentType, ContextType, Partial>; infrastructure?: Resolver, ParentType, ContextType, RequireFields>; infrastructures?: Resolver, ParentType, ContextType, Partial>; + ingestionCsv?: Resolver, ParentType, ContextType, RequireFields>; + ingestionCsvs?: Resolver, ParentType, ContextType, Partial>; ingestionRss?: Resolver, ParentType, ContextType, RequireFields>; ingestionRsss?: Resolver, ParentType, ContextType, Partial>; ingestionTaxii?: Resolver, ParentType, ContextType, RequireFields>; @@ -34233,6 +34385,7 @@ export type QueryResolvers, ParentType, ContextType, Partial>; taxiiCollection?: Resolver, ParentType, ContextType, RequireFields>; taxiiCollections?: Resolver, ParentType, ContextType, Partial>; + test_mapper?: Resolver, ParentType, ContextType, RequireFields>; threatActor?: Resolver, ParentType, ContextType, Partial>; threatActorGroup?: Resolver, ParentType, ContextType, Partial>; threatActorIndividual?: Resolver, ParentType, ContextType, RequireFields>; @@ -37109,6 +37262,9 @@ export type Resolvers = ResolversObject<{ InfrastructureConnection?: InfrastructureConnectionResolvers; InfrastructureEdge?: InfrastructureEdgeResolvers; InfrastructureEditMutations?: InfrastructureEditMutationsResolvers; + IngestionCsv?: IngestionCsvResolvers; + IngestionCsvConnection?: IngestionCsvConnectionResolvers; + IngestionCsvEdge?: IngestionCsvEdgeResolvers; IngestionRss?: IngestionRssResolvers; IngestionRssConnection?: IngestionRssConnectionResolvers; IngestionRssEdge?: IngestionRssEdgeResolvers; diff --git a/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts b/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts index 4b47018db1c61..bbc04e6e589f4 100644 --- a/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts +++ b/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts @@ -1,6 +1,7 @@ import type { convertableToString } from 'xml2js'; import { parseStringPromise as xmlParse } from 'xml2js'; import TurndownService from 'turndown'; +import bcrypt from 'bcryptjs'; import * as R from 'ramda'; import { v4 as uuidv4 } from 'uuid'; import { clearIntervalAsync, setIntervalAsync } from 'set-interval-async/dynamic'; @@ -20,9 +21,15 @@ import { pushToSync } from '../database/rabbitmq'; import { OPENCTI_SYSTEM_UUID } from '../schema/general'; import { findAllRssIngestions, patchRssIngestion } from '../modules/ingestion/ingestion-rss-domain'; import type { AuthContext } from '../types/user'; -import type { BasicStoreEntityIngestionRss, BasicStoreEntityIngestionTaxii } from '../modules/ingestion/ingestion-types'; +import type { BasicStoreEntityIngestionCsv, BasicStoreEntityIngestionRss, BasicStoreEntityIngestionTaxii } from '../modules/ingestion/ingestion-types'; import { findAllTaxiiIngestions, patchTaxiiIngestion } from '../modules/ingestion/ingestion-taxii-domain'; import { TaxiiVersion } from '../generated/graphql'; +import { fetchCsvFromUrl, findAllCsvIngestions, patchCsvIngestion, testCsvIngestionMapping } from '../modules/ingestion/ingestion-csv-domain'; +import type { BasicStoreEntityCsvMapper } from '../modules/internal/csvMapper/csvMapper-types'; +import { findById } from '../modules/internal/csvMapper/csvMapper-domain'; +import { bundleProcess } from '../parser/csv-bundler'; +import { createWork, updateExpectationsNumber } from '../domain/work'; +import { IMPORT_CSV_CONNECTOR } from '../connector/importCsv/importCsv'; // Ingestion manager responsible to cleanup old data // Each API will start is ingestion manager. @@ -276,6 +283,101 @@ const taxiiExecutor = async (context: AuthContext) => { }; // endregion +// region Csv ingestion +interface CsvResponseData { + data: string, + addedLast: string | undefined | null +} +const csvHttpGet = async (ingestion: BasicStoreEntityIngestionCsv): Promise => { + const headers = new AxiosHeaders(); + headers.Accept = 'application/csv'; + if (ingestion.authentication_type === 'basic') { + const auth = Buffer.from(ingestion.authentication_value, 'utf-8').toString('base64'); + headers.Authorization = `Basic ${auth}`; + } + if (ingestion.authentication_type === 'bearer') { + headers.Authorization = `Bearer ${ingestion.authentication_value}`; + } + let certificates; + if (ingestion.authentication_type === 'certificate') { + const [cert, key, ca] = ingestion.authentication_value.split(':'); + certificates = { cert, key, ca }; + } + const httpClientOptions: GetHttpClient = { headers, rejectUnauthorized: false, responseType: 'json', certificates }; + const httpClient = getHttpClient(httpClientOptions); + const { data, headers: resultHeaders } = await httpClient.get(ingestion.uri); + return { data, addedLast: resultHeaders['x-csv-date-added-last'] }; +}; +const csvDataToObjects = async (data: string, ingestion: BasicStoreEntityIngestionCsv, csvMapper: BasicStoreEntityCsvMapper, context: AuthContext) => { + const entitiesData = data.split('\n'); + const csvBuffer = await fetchCsvFromUrl(ingestion.uri, csvMapper.skipLineChar); + const { objects } = await bundleProcess(context, context.user ?? SYSTEM_USER, csvBuffer, csvMapper); + if (objects === undefined) { + const error = UnknownError('Undefined CSV objects', data); + logApp.error(error, { name: ingestion.name, context: 'CSV transform' }); + } + logApp.info(`[OPENCTI-MODULE] CSV ingestion execution for ${entitiesData.length} items`); + return objects; +}; + +const csvDataHandler = async (context: AuthContext, ingestion: BasicStoreEntityIngestionCsv) => { + const { data, addedLast } = await csvHttpGet(ingestion); + const user = context.user ?? SYSTEM_USER; + const csvMapper: BasicStoreEntityCsvMapper = await findById(context, user, ingestion.csv_mapper_id); + const csvMappingTestResult = await testCsvIngestionMapping(context, user, ingestion.uri, ingestion.csv_mapper_id); + + if (!csvMappingTestResult.nbEntities) { + const error = UnknownError('Invalid data from URL', data); + logApp.error(error, { name: ingestion.name, context: 'CSV transform' }); + } + + const isUnchangedData = bcrypt.compareSync(data, ingestion.current_state_hash ?? ''); + if (isUnchangedData) { + return; + } + + const objects = await csvDataToObjects(data, ingestion, csvMapper, context); + const bundleSize = 1000; + for (let index = 0; index < objects.length; index += bundleSize) { + // Filter objects already added to queue + const splitBundle = objects.slice(index, index + bundleSize); + const bundle = { type: 'bundle', id: `bundle--${uuidv4()}`, objects: splitBundle }; + const stixBundle = JSON.stringify(bundle); + const content = Buffer.from(stixBundle, 'utf-8').toString('base64'); + const friendlyName = 'CSV feed Ingestion'; + const work = await createWork(context, user, IMPORT_CSV_CONNECTOR, friendlyName, IMPORT_CSV_CONNECTOR.id) as unknown as Work; + await updateExpectationsNumber(context, user, work.id, 1); + await pushToSync({ type: 'bundle', applicant_id: ingestion.user_id ?? OPENCTI_SYSTEM_UUID, work_id: work.id, content, update: true }); + } + + // Update the state + const hashedIncomingData = bcrypt.hashSync(data); + await patchCsvIngestion(context, SYSTEM_USER, ingestion.internal_id, { + current_state_hash: hashedIncomingData, + added_after_start: utcDate(addedLast) + }); +}; +const csvExecutor = async (context: AuthContext) => { + const filters = { + mode: 'and', + filters: [{ key: 'ingestion_running', values: [true] }], + filterGroups: [], + }; + const opts = { filters, connectionFormat: false, noFiltersChecking: true }; + const ingestions = await findAllCsvIngestions(context, SYSTEM_USER, opts); + const ingestionPromises = []; + for (let i = 0; i < ingestions.length; i += 1) { + const ingestion = ingestions[i]; + const ingestionPromise = csvDataHandler(context, ingestion) + .catch((e) => { + logApp.error(`[OPENCTI-MODULE] execution error for ${ingestion.name} : ${e}`, { error: e }); + }); + ingestionPromises.push(ingestionPromise); + } + return Promise.all(ingestionPromises); +}; +// endregion + const ingestionHandler = async () => { logApp.debug('[OPENCTI-MODULE] Running ingestion manager'); let lock; @@ -289,6 +391,7 @@ const ingestionHandler = async () => { const ingestionPromises = []; ingestionPromises.push(rssExecutor(context, turndownService)); ingestionPromises.push(taxiiExecutor(context)); + ingestionPromises.push(csvExecutor(context)); await Promise.all(ingestionPromises); } catch (e: any) { // We dont care about failing to get the lock. diff --git a/opencti-platform/opencti-graphql/src/modules/index.ts b/opencti-platform/opencti-graphql/src/modules/index.ts index 55ff278968f57..6d5b1af12dbf6 100644 --- a/opencti-platform/opencti-graphql/src/modules/index.ts +++ b/opencti-platform/opencti-graphql/src/modules/index.ts @@ -52,6 +52,7 @@ import './threatActorIndividual/threatActorIndividual'; import './playbook/playbook'; import './ingestion/ingestion-rss'; import './ingestion/ingestion-taxii'; +import './ingestion/ingestion-csv'; import './indicator/indicator'; import './organization/organization'; import './internal/csvMapper/csvMapper'; @@ -86,6 +87,7 @@ import './threatActorIndividual/threatActorIndividual-graphql'; import './playbook/playbook-graphql'; import './ingestion/ingestion-rss-graphql'; import './ingestion/ingestion-taxii-graphql'; +import './ingestion/ingestion-csv-graphql'; import './indicator/indicator-graphql'; import './organization/organization-graphql'; import './internal/csvMapper/csvMapper-graphql'; diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-converter.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-converter.ts index 9f439b77f9f64..b79cd4c7f078d 100644 --- a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-converter.ts +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-converter.ts @@ -1,6 +1,6 @@ import { STIX_EXT_OCTI } from '../../types/stix-extensions'; import { buildStixObject, cleanObject } from '../../database/stix-converter'; -import type { StixIngestionRss, StixIngestionTaxii, StoreEntityIngestionRss } from './ingestion-types'; +import type { StixIngestionCsv, StixIngestionRss, StixIngestionTaxii, StoreEntityIngestionCsv, StoreEntityIngestionRss } from './ingestion-types'; export const convertIngestionRssToStix = (instance: StoreEntityIngestionRss): StixIngestionRss => { const stixObject = buildStixObject(instance); @@ -36,3 +36,21 @@ export const convertIngestionTaxiiToStix = (instance: StoreEntityIngestionRss): } }; }; + +export const convertIngestionCsvToStix = (instance: StoreEntityIngestionCsv): StixIngestionCsv => { + const stixObject = buildStixObject(instance); + return { + ...stixObject, + name: instance.name, + description: instance.description, + uri: instance.uri, + csv_mapper_id: instance.csv_mapper_id, + ingestion_running: instance.ingestion_running, + extensions: { + [STIX_EXT_OCTI]: cleanObject({ + ...stixObject.extensions[STIX_EXT_OCTI], + extension_type: 'new-sdo', + }) + } + }; +}; diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-domain.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-domain.ts new file mode 100644 index 0000000000000..43ab25679d29e --- /dev/null +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-domain.ts @@ -0,0 +1,112 @@ +import axios from 'axios'; +import type { AuthContext, AuthUser } from '../../types/user'; +import { listAllEntities, listEntitiesPaginated, storeLoadById } from '../../database/middleware-loader'; +import { type BasicStoreEntityIngestionCsv, ENTITY_TYPE_INGESTION_CSV } from './ingestion-types'; +import { createEntity, deleteElementById, patchAttribute, updateAttribute } from '../../database/middleware'; +import { publishUserAction } from '../../listener/UserActionListener'; +import type { CsvMapperTestResult, EditInput, IngestionCsvAddInput } from '../../generated/graphql'; +import { notify } from '../../database/redis'; +import { BUS_TOPICS } from '../../config/conf'; +import { ABSTRACT_INTERNAL_OBJECT } from '../../schema/general'; +import { type BasicStoreEntityCsvMapper, ENTITY_TYPE_CSV_MAPPER } from '../internal/csvMapper/csvMapper-types'; +import { bundleProcess } from '../../parser/csv-bundler'; +import { findById as findCsvMapperById } from '../internal/csvMapper/csvMapper-domain'; + +export const findById = (context: AuthContext, user: AuthUser, ingestionId: string) => { + return storeLoadById(context, user, ingestionId, ENTITY_TYPE_INGESTION_CSV); +}; + +// findLastCSVIngestion + +export const findAllPaginated = async (context: AuthContext, user: AuthUser, opts = {}) => { + return listEntitiesPaginated(context, user, [ENTITY_TYPE_INGESTION_CSV], opts); +}; + +export const findAllCsvIngestions = async (context: AuthContext, user: AuthUser, opts = {}) => { + return listAllEntities(context, user, [ENTITY_TYPE_INGESTION_CSV], opts); +}; + +export const findCsvMapperForIngestionById = (context: AuthContext, user: AuthUser, csvMapperId: string) => { + return storeLoadById(context, user, csvMapperId, ENTITY_TYPE_CSV_MAPPER); +}; + +export const addIngestionCsv = async (context: AuthContext, user: AuthUser, input: IngestionCsvAddInput) => { + const { element, isCreation } = await createEntity(context, user, input, ENTITY_TYPE_INGESTION_CSV, { complete: true }); + if (isCreation) { + await publishUserAction({ + user, + event_type: 'mutation', + event_scope: 'create', + event_access: 'administration', + message: `creates csv ingestion \`${input.name}\``, + context_data: { id: element.id, entity_type: ENTITY_TYPE_INGESTION_CSV, input } + }); + } + return element; +}; + +export const patchCsvIngestion = async (context: AuthContext, user: AuthUser, id: string, patch: object) => { + const patched = await patchAttribute(context, user, id, ENTITY_TYPE_INGESTION_CSV, patch); + return patched.element; +}; + +export const ingestionCsvEditField = async (context: AuthContext, user: AuthUser, ingestionId: string, input: EditInput[]) => { + const { element } = await updateAttribute(context, user, ingestionId, ENTITY_TYPE_INGESTION_CSV, input); + await publishUserAction({ + user, + event_type: 'mutation', + event_scope: 'update', + event_access: 'administration', + message: `updates \`${input.map((i) => i.key).join(', ')}\` for csv ingestion \`${element.name}\``, + context_data: { id: ingestionId, entity_type: ENTITY_TYPE_INGESTION_CSV, input } + }); + return notify(BUS_TOPICS[ABSTRACT_INTERNAL_OBJECT].EDIT_TOPIC, element, user); +}; + +export const deleteIngestionCsv = async (context: AuthContext, user: AuthUser, ingestionId: string) => { + const deleted = await deleteElementById(context, user, ingestionId, ENTITY_TYPE_INGESTION_CSV); + await publishUserAction({ + user, + event_type: 'mutation', + event_scope: 'delete', + event_access: 'administration', + message: `deletes csv ingestion \`${deleted.name}\``, + context_data: { id: ingestionId, entity_type: ENTITY_TYPE_INGESTION_CSV, input: deleted } + }); + return ingestionId; +}; + +export const fetchCsvFromUrl = async (url: string, csvMapperSkipLineChar: string | undefined): Promise => { + const response = await axios.get(url, { responseType: 'arraybuffer' }); + const dataExtract = response.data.toString().split('\n') + .filter((line: string) => ( + (!!csvMapperSkipLineChar && !line.startsWith(csvMapperSkipLineChar)) + || (!csvMapperSkipLineChar && !!line) + )) + .join('\n'); + return Buffer.from(dataExtract); +}; + +export const fetchCsvExtractFromUrl = async (url: string, csvMapperSkipLineChar: string | undefined): Promise => { + const response = await axios.get(url, { responseType: 'arraybuffer' }); + const TEST_LIMIT = 50; + const dataExtract = response.data.toString().split('\n') + .filter((line: string) => ( + (!!csvMapperSkipLineChar && !line.startsWith(csvMapperSkipLineChar)) + || (!csvMapperSkipLineChar && !!line) + )) + .slice(0, TEST_LIMIT) + .join('\n'); + return Buffer.from(dataExtract); +}; + +export const testCsvIngestionMapping = async (context: AuthContext, user: AuthUser, uri: string, csv_mapper_id: string): Promise => { + const csvMapper: BasicStoreEntityCsvMapper = await findCsvMapperById(context, user, csv_mapper_id); + const csvBuffer = await fetchCsvExtractFromUrl(uri, csvMapper.skipLineChar); + const bundle = await bundleProcess(context, user, csvBuffer, csvMapper); + return { + objects: JSON.stringify(bundle.objects, null, 2), + nbRelationships: bundle.objects.filter((object) => object.type === 'relationship').length, + nbEntities: bundle.objects.filter((object) => object.type !== 'relationship').length, + }; +}; diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-graphql.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-graphql.ts new file mode 100644 index 0000000000000..8cbfcdce7d0a2 --- /dev/null +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-graphql.ts @@ -0,0 +1,8 @@ +import { registerGraphqlSchema } from '../../graphql/schema'; +import ingestionCsvResolvers from './ingestion-csv-resolver'; +import ingestionTypeDefs from './ingestion-csv.graphql'; + +registerGraphqlSchema({ + schema: ingestionTypeDefs, + resolver: ingestionCsvResolvers, +}); diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-resolver.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-resolver.ts new file mode 100644 index 0000000000000..ce2c1d09ebda5 --- /dev/null +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv-resolver.ts @@ -0,0 +1,39 @@ +import { batchLoader } from '../../database/middleware'; +import { batchCreator } from '../../domain/user'; +import type { Resolvers } from '../../generated/graphql'; +import { + addIngestionCsv, + deleteIngestionCsv, + findAllPaginated, + findById, + findCsvMapperForIngestionById, + ingestionCsvEditField, + testCsvIngestionMapping +} from './ingestion-csv-domain'; + +const creatorLoader = batchLoader(batchCreator); + +const ingestionCsvResolvers: Resolvers = { + Query: { + ingestionCsv: (_, { id }, context) => findById(context, context.user, id), + ingestionCsvs: (_, args, context) => findAllPaginated(context, context.user, args), + test_mapper: (_, { uri, csv_mapper_id }, context) => testCsvIngestionMapping(context, context.user, uri, csv_mapper_id), + }, + IngestionCsv: { + user: (ingestionCsv, _, context) => creatorLoader.load(ingestionCsv.user_id, context, context.user), + csvMapper: (ingestionCsv, _, context) => findCsvMapperForIngestionById(context, context.user, ingestionCsv.csv_mapper_id), + }, + Mutation: { + ingestionCsvAdd: (_, { input }, context) => { + return addIngestionCsv(context, context.user, input); + }, + ingestionCsvDelete: (_, { id }, context) => { + return deleteIngestionCsv(context, context.user, id); + }, + ingestionCsvFieldPatch: (_, { id, input }, context) => { + return ingestionCsvEditField(context, context.user, id, input); + }, + }, +}; + +export default ingestionCsvResolvers; diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql new file mode 100644 index 0000000000000..6a8e68f65cbde --- /dev/null +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql @@ -0,0 +1,81 @@ +enum CsvAuthType { + none + basic + bearer + certificate +} + +type IngestionCsv implements InternalObject & BasicObject { + id: ID! + entity_type: String! + standard_id: String! + parent_types: [String]! + created_at: DateTime + updated_at: DateTime + name: String! + description: String + uri: String! + csvMapper: CsvMapper! + authentication_type: CsvAuthType! + authentication_value: String + user_id: String! + user: Creator + ingestion_running: Boolean + current_state_hash: String + current_state_date: DateTime +} + +enum IngestionCsvOrdering { + name + created_at + updated_at + uri + mapper +} + +type IngestionCsvConnection { + pageInfo: PageInfo! + edges: [IngestionCsvEdge!]! +} + +type IngestionCsvEdge { + cursor: String! + node: IngestionCsv! +} + +# Queries +type Query { + ingestionCsv(id: String!): IngestionCsv @auth(for: [SETTINGS]) + ingestionCsvs( + first: Int + after: ID + orderBy: IngestionCsvOrdering + orderMode: OrderingMode + filters: FilterGroup + includeAuthorities: Boolean + search: String + ): IngestionCsvConnection @auth(for: [SETTINGS]) + test_mapper( + uri: String! + csv_mapper_id: String! + ): CsvMapperTestResult @auth(for: [SETTINGS]) +} + +# Mutations +input IngestionCsvAddInput { + name: String! @constraint(minLength: 2) + description: String + authentication_type: CsvAuthType! + authentication_value: String + current_state_date: DateTime + uri: String! @constraint(minLength: 5) + csv_mapper_id: String! + ingestion_running: Boolean + user_id: String! +} + +type Mutation { + ingestionCsvAdd(input: IngestionCsvAddInput!): IngestionCsv @auth(for: [SETTINGS]) + ingestionCsvDelete(id: ID!): ID @auth(for: [SETTINGS]) + ingestionCsvFieldPatch(id: ID!, input: [EditInput!]!): IngestionCsv @auth(for: [SETTINGS]) +} \ No newline at end of file diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts new file mode 100644 index 0000000000000..b8c37ec96fb7a --- /dev/null +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts @@ -0,0 +1,42 @@ +import { v4 as uuidv4 } from 'uuid'; +import { type ModuleDefinition, registerDefinition } from '../../schema/module'; +import { ENTITY_TYPE_INGESTION_CSV, type StixIngestionCsv, type StoreEntityIngestionCsv } from './ingestion-types'; +import { ABSTRACT_INTERNAL_OBJECT } from '../../schema/general'; +import { normalizeName } from '../../schema/identifier'; +import { convertIngestionCsvToStix } from './ingestion-converter'; + +const INGESTION_CSV_DEFINITION: ModuleDefinition = { + type: { + id: 'ingestion-csv', + name: ENTITY_TYPE_INGESTION_CSV, + category: ABSTRACT_INTERNAL_OBJECT, + aliased: false + }, + identifier: { + definition: { + [ENTITY_TYPE_INGESTION_CSV]: () => uuidv4(), + }, + resolvers: { + name(data: object) { + return normalizeName(data); + }, + }, + }, + attributes: [ + { name: 'name', label: 'Name', type: 'string', format: 'short', mandatoryType: 'external', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'description', label: 'Description', type: 'string', format: 'short', mandatoryType: 'customizable', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'uri', label: 'Uri', type: 'string', format: 'short', mandatoryType: 'customizable', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'user_id', label: 'User_id', type: 'string', format: 'id', entityTypes: ['user'], mandatoryType: 'external', editDefault: false, multiple: false, upsert: true, isFilterable: true }, + { name: 'csv_mapper_id', label: 'Csv_mapper_id', type: 'string', format: 'id', entityTypes: ['csvMapper'], mandatoryType: 'external', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'ingestion_running', label: 'Ingestion_running', type: 'boolean', mandatoryType: 'external', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'added_after_start', label: 'Added_after_start', type: 'date', mandatoryType: 'external', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + { name: 'current_state_hash', label: 'Current_state_hash', type: 'string', format: 'short', mandatoryType: 'external', editDefault: true, multiple: false, upsert: true, isFilterable: true }, + ], + relations: [], + representative: (stix: StixIngestionCsv) => { + return stix.name; + }, + converter: convertIngestionCsvToStix +}; + +registerDefinition(INGESTION_CSV_DEFINITION); diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-types.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-types.ts index b4ea96732cfeb..1f897bb686b6d 100644 --- a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-types.ts +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-types.ts @@ -1,6 +1,7 @@ import type { StixObject, StixOpenctiExtensionSDO } from '../../types/stix-common'; import { STIX_EXT_OCTI } from '../../types/stix-extensions'; import type { StoreEntity, BasicStoreEntity } from '../../types/store'; +import type { CsvMapper } from '../../generated/graphql'; // region Rss ingestion export const ENTITY_TYPE_INGESTION_RSS = 'IngestionRss'; @@ -71,3 +72,39 @@ export interface StixIngestionTaxii extends StixObject { } } // endregion + +// region Csv ingestion +export const ENTITY_TYPE_INGESTION_CSV = 'IngestionCsv'; + +export interface BasicStoreEntityIngestionCsv extends BasicStoreEntity { + current_state_hash: string; + name: string + description: string + uri: string + csvMapper: CsvMapper + csv_mapper_id: string + authentication_type: 'none' | 'basic' | 'bearer' | 'certificate' + authentication_value: string + user_id: string | undefined + ingestion_running: boolean +} + +export interface StoreEntityIngestionCsv extends StoreEntity { + name: string + description: string + uri: string + csv_mapper_id: string + ingestion_running: boolean +} + +export interface StixIngestionCsv extends StixObject { + name: string + description: string + uri: string + csv_mapper_id: string + ingestion_running: boolean + extensions: { + [STIX_EXT_OCTI]: StixOpenctiExtensionSDO + } +} +// endregion diff --git a/opencti-platform/opencti-graphql/src/modules/internal/csvMapper/csvMapper-utils.ts b/opencti-platform/opencti-graphql/src/modules/internal/csvMapper/csvMapper-utils.ts index 5bc0e96ffca13..1ca8e67b07fa1 100644 --- a/opencti-platform/opencti-graphql/src/modules/internal/csvMapper/csvMapper-utils.ts +++ b/opencti-platform/opencti-graphql/src/modules/internal/csvMapper/csvMapper-utils.ts @@ -46,7 +46,7 @@ export const parseCsvMapper = (entity: any): BasicStoreEntityCsvMapper => { }; export const parseCsvMapperWithDefaultValues = async (context: AuthContext, user: AuthUser, entity: any) => { - if (typeof entity.representations !== 'string') { + if (typeof entity?.representations !== 'string') { return entity; } diff --git a/opencti-platform/opencti-graphql/src/parser/csv-parser.ts b/opencti-platform/opencti-graphql/src/parser/csv-parser.ts index 86caf532ebfa7..46457f040bcc5 100644 --- a/opencti-platform/opencti-graphql/src/parser/csv-parser.ts +++ b/opencti-platform/opencti-graphql/src/parser/csv-parser.ts @@ -35,13 +35,12 @@ const parseCsvFile = (filePath: string, delimiter: string): Promise }); }; -const parseCsvBufferContent = (buffer: Buffer, delimiter: string): Promise => { +export const parseCsvBufferContent = (buffer: Buffer, delimiter: string): Promise => { return new Promise((resolve, reject) => { const readable = Readable.from(buffer); const chunks: Uint8Array[] = []; readable.on('data', (chunk: Uint8Array) => { - const returnLine = new TextEncoder().encode('\n'); - chunks.push(new Uint8Array([...chunk, ...returnLine])); + chunks.push(new Uint8Array([...chunk])); }) .on('error', (err) => { reject(err); diff --git a/opencti-platform/opencti-graphql/src/types/work.d.ts b/opencti-platform/opencti-graphql/src/types/work.d.ts new file mode 100644 index 0000000000000..3b1c0ae6b30b1 --- /dev/null +++ b/opencti-platform/opencti-graphql/src/types/work.d.ts @@ -0,0 +1,22 @@ +interface Work { + _index: string + id: string + sort: number[] + internal_id: string + timestamp: string + updated_at: string + name: string + entity_type: string + event_type: string + event_source_id: string + user_id: string + connector_id: string + status: string + import_expected_number: number + received_time: string | null + processed_time: string | null + completed_time: string | null + completed_number: string + messages: string[] + errors: string[] +} diff --git a/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/ingestion-csv-test.ts b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/ingestion-csv-test.ts new file mode 100644 index 0000000000000..a8d13e791329f --- /dev/null +++ b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/ingestion-csv-test.ts @@ -0,0 +1,167 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import gql from 'graphql-tag'; +import { ADMIN_USER, queryAsAdmin } from '../../utils/testQuery'; + +describe('CSV ingestion resolver standard behavior', () => { + let singleColumnCsvMapperId = ''; + let singleColumnCsvFeedIngesterId = ''; + + beforeAll(async () => { + const SINGLE_COLUMN_CSV_MAPPER = { + input: { + has_header: false, + name: 'Single column CSV mapper', + separator: ',', + representations: '[{"id":"75c3c21c-0a92-497f-962d-4e6e1a488481","type":"entity","target":{"entity_type":"IPv4-Addr"},"attributes":[{"key":"value","column":{"column_name":"A"},"based_on":null}]}]', + skipLineChar: '' + } + }; + + const createSingleColumnCsvMapperQueryResult = await queryAsAdmin({ + query: gql` + mutation createSingleColumnCsvMapper($input: CsvMapperAddInput!) { + csvMapperAdd(input: $input) { + id + } + }, + `, + variables: SINGLE_COLUMN_CSV_MAPPER + }); + + singleColumnCsvMapperId = createSingleColumnCsvMapperQueryResult?.data?.csvMapperAdd?.id; + }); + + it('should create a CSV feeds ingester', async () => { + const CSV_FEED_INGESTER_TO_CREATE = { + input: { + authentication_type: 'none', + name: 'Single column', + uri: 'https://lists.blocklist.de/lists/all.txt', + csv_mapper_id: singleColumnCsvMapperId, + user_id: ADMIN_USER.id + } + }; + const createSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation createSingleColumnCsvFeedsIngester($input: IngestionCsvAddInput!) { + ingestionCsvAdd(input: $input) { + id + entity_type + ingestion_running + } + }, + `, + variables: CSV_FEED_INGESTER_TO_CREATE + }); + singleColumnCsvFeedIngesterId = createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.id; + expect(singleColumnCsvFeedIngesterId).toBeDefined(); + expect(createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.entity_type).toBe('IngestionCsv'); + expect(createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.ingestion_running).toBeFalsy(); + }); + + it('should create a CSV feeds ingester with authentication', async () => { + const CSV_FEED_INGESTER_TO_CREATE = { + input: { + authentication_type: 'none', + name: 'Single column', + uri: 'https://lists.blocklist.de/lists/all.txt', + csv_mapper_id: singleColumnCsvMapperId, + user_id: ADMIN_USER.id + } + }; + const createSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation createSingleColumnCsvFeedsIngester($input: IngestionCsvAddInput!) { + ingestionCsvAdd(input: $input) { + id + entity_type + ingestion_running + } + }, + `, + variables: CSV_FEED_INGESTER_TO_CREATE + }); + singleColumnCsvFeedIngesterId = createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.id; + expect(singleColumnCsvFeedIngesterId).toBeDefined(); + expect(createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.entity_type).toBe('IngestionCsv'); + expect(createSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvAdd?.ingestion_running).toBeFalsy(); + }); + + it('should start the CSV feeds ingester', async () => { + const CSV_FEED_INGESTER_TO_START = { + id: singleColumnCsvFeedIngesterId, + input: { + key: 'ingestion_running', + value: [true], + } + }; + const startSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation startSingleColumnCsvFeedsIngester($id: ID!, $input: [EditInput!]!) { + ingestionCsvFieldPatch(id: $id, input: $input){ + ingestion_running + } + } + `, + variables: CSV_FEED_INGESTER_TO_START + }); + expect(startSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvFieldPatch?.ingestion_running).toBeTruthy(); + }); + + it('should stop the CSV feeds ingester', async () => { + const CSV_FEED_INGESTER_TO_STOP = { + id: singleColumnCsvFeedIngesterId, + input: { + key: 'ingestion_running', + value: [false], + } + }; + const stopSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation stopSingleColumnCsvFeedsIngester($id: ID!, $input: [EditInput!]!) { + ingestionCsvFieldPatch(id: $id, input: $input){ + ingestion_running + } + } + `, + variables: CSV_FEED_INGESTER_TO_STOP + }); + expect(stopSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvFieldPatch?.ingestion_running).toBeFalsy(); + }); + + it('should update the CSV feeds ingester', async () => { + const CSV_FEED_INGESTER_TO_UPDATE = { + id: singleColumnCsvFeedIngesterId, + input: { + key: 'name', + value: ['Single column CSV feed ingester'], + } + }; + const stopSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation stopSingleColumnCsvFeedsIngester($id: ID!, $input: [EditInput!]!) { + ingestionCsvFieldPatch(id: $id, input: $input){ + name + } + } + `, + variables: CSV_FEED_INGESTER_TO_UPDATE + }); + expect(stopSingleColumnCsvFeedsIngesterQueryResult?.data?.ingestionCsvFieldPatch?.name).toBe('Single column CSV feed ingester'); + }); + + it('should delete the CSV feeds ingester', async () => { + const CSV_FEED_INGESTER_TO_DELETE = { + id: singleColumnCsvFeedIngesterId, + }; + const deleteSingleColumnCsvFeedsIngesterQueryResultSingleColumnCsvFeedsIngesterQueryResult = await queryAsAdmin({ + query: gql` + mutation deleteSingleColumnCsvFeedsIngesterQueryResultSingleColumnCsvFeedsIngester($id: ID!) { + ingestionCsvDelete(id: $id) + } + `, + variables: CSV_FEED_INGESTER_TO_DELETE + }); + expect(deleteSingleColumnCsvFeedsIngesterQueryResultSingleColumnCsvFeedsIngesterQueryResult.data?.ingestionCsvDelete).toBe(singleColumnCsvFeedIngesterId); + }); +});