From fcf2a9c0053b160ab3165202ff941613a3493566 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Mon, 27 Jan 2025 10:33:41 +0100 Subject: [PATCH] Merge branch 'master' into release/6.5.0 --- .drone.yml | 10 +- opencti-platform/opencti-front/package.json | 27 +- .../src/components/MarkdownDisplay.tsx | 29 +-- .../src/components/list_lines/ListLines.jsx | 19 +- .../analyses/groupings/GroupingCreation.tsx | 2 +- .../MalwareAnalysisCreation.tsx | 26 +- .../analyses/reports/ReportCreation.tsx | 4 +- .../arsenal/channels/ChannelCreation.tsx | 22 +- .../arsenal/malwares/MalwareCreation.tsx | 2 +- .../components/arsenal/tools/ToolCreation.tsx | 2 +- .../vulnerabilities/VulnerabilityCreation.tsx | 30 +-- .../case_incidents/CaseIncidentCreation.tsx | 2 +- .../cases/case_rfis/CaseRfiCreation.tsx | 2 +- .../cases/case_rfts/CaseRftCreation.tsx | 2 +- .../components/common/charts/Chart.tsx | 64 +++-- .../ContainerAddStixCoreObjects.jsx | 15 +- .../StixCoreObjectHistory.jsx | 189 -------------- .../StixCoreObjectHistory.tsx | 175 +++++++++++++ .../StixCoreObjectLatestHistory.jsx | 113 --------- .../StixCoreObjectLatestHistory.tsx | 107 ++++++++ .../EntityStixCoreRelationships.tsx | 7 +- .../StixCoreRelationshipCreationForm.js | 2 +- ...StixCoreRelationshipsRelationshipsView.tsx | 4 +- .../StixDomainObjectCreation.jsx | 4 +- .../StixDomainObjectVictimology.jsx | 51 ++-- .../StixDomainObjectVictimologyRegions.jsx | 9 - .../StixDomainObjectVictimologySectors.jsx | 7 +- .../private/components/data/IngestionCsv.tsx | 9 +- .../private/components/data/IngestionRss.jsx | 9 +- .../data/ingestionCsv/IngestionCsvLine.tsx | 20 +- .../data/ingestionRss/IngestionRssLine.jsx | 22 +- .../entities/events/EventCreation.tsx | 6 +- .../individuals/IndividualCreation.tsx | 2 +- .../entities/sectors/SectorCreation.tsx | 2 +- .../entities/systems/SystemCreation.tsx | 2 +- .../events/incidents/IncidentCreation.tsx | 4 +- .../observed_data/ObservedDataCreation.tsx | 8 +- .../StixSightingRelationshipCreationForm.js | 2 +- .../AdministrativeAreaCreation.tsx | 6 +- .../locations/cities/CityCreation.tsx | 6 +- .../locations/cities/CityKnowledge.tsx | 2 + .../locations/countries/CountryCreation.tsx | 12 +- .../locations/positions/PositionCreation.tsx | 10 +- .../locations/regions/RegionCreation.tsx | 12 +- .../src/private/components/nav/LeftBar.jsx | 24 +- .../indicators/IndicatorCreation.tsx | 10 +- .../InfrastructureCreation.tsx | 8 +- .../attack_patterns/AttackPatternCreation.tsx | 4 +- .../CourseOfActionCreation.tsx | 2 +- .../data_components/DataComponentCreation.tsx | 22 +- .../data_sources/DataSourceCreation.tsx | 22 +- .../threats/campaigns/CampaignCreation.tsx | 2 +- .../intrusion_sets/IntrusionSetCreation.tsx | 2 +- .../intrusion_sets/IntrusionSetDetails.jsx | 230 ------------------ .../intrusion_sets/IntrusionSetDetails.tsx | 226 +++++++++++++++++ .../ThreatActorGroupCreation.tsx | 6 +- .../src/schema/relay.schema.graphql | 2 + opencti-platform/opencti-front/yarn.lock | 198 +++++++-------- .../opencti-graphql/config/default.json | 9 +- opencti-platform/opencti-graphql/package.json | 14 +- .../opencti-graphql/src/database/engine.js | 57 ++++- .../src/database/middleware.js | 24 +- .../opencti-graphql/src/domain/city.js | 7 +- .../opencti-graphql/src/generated/graphql.ts | 4 + .../src/manager/ingestionManager.ts | 52 +++- .../1733912620469-clean-deprecated-rels.js | 168 +++++++++++++ .../modules/ingestion/ingestion-csv.graphql | 1 + .../src/modules/ingestion/ingestion-csv.ts | 1 + .../modules/ingestion/ingestion-rss.graphql | 1 + .../src/modules/ingestion/ingestion-rss.ts | 1 + .../src/modules/ingestion/ingestion-types.ts | 4 + .../02-integration/02-resolvers/city-test.js | 1 - opencti-platform/opencti-graphql/yarn.lock | 94 +++---- 73 files changed, 1236 insertions(+), 1020 deletions(-) delete mode 100644 opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectHistory.jsx create mode 100644 opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectHistory.tsx delete mode 100644 opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectLatestHistory.jsx create mode 100644 opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectLatestHistory.tsx delete mode 100644 opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetDetails.jsx create mode 100644 opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetDetails.tsx create mode 100644 opencti-platform/opencti-graphql/src/migrations/1733912620469-clean-deprecated-rels.js diff --git a/.drone.yml b/.drone.yml index 56ec3921bca3..8b1d2b7732bf 100644 --- a/.drone.yml +++ b/.drone.yml @@ -123,11 +123,11 @@ steps: - npm install -g jfrog-cli-v2-jf # Collect git info - jf rt bag $JFROG_BUILD_NAME $DRONE_BUILD_NUMBER - # Archive and upload each artefact that we need - - tar -czvf frontend-test-results-$DRONE_BUILD_NUMBER.tar.gz opencti-platform/opencti-front/test-results - - jf rt u frontend-test-results-$DRONE_BUILD_NUMBER.tar.gz $JFROG_REPOSITORY --build-name=$JFROG_BUILD_NAME --build-number=$DRONE_BUILD_NUMBER --url=$JFROG_URL --access-token=$JFROG_TOKEN - - tar -czvf backend-test-results-$DRONE_BUILD_NUMBER.tar.gz opencti-platform/opencti-graphql/test-results - - jf rt u backend-test-results-$DRONE_BUILD_NUMBER.tar.gz $JFROG_REPOSITORY --build-name=$JFROG_BUILD_NAME --build-number=$DRONE_BUILD_NUMBER --url=$JFROG_URL --access-token=$JFROG_TOKEN + # Archive and upload each artefact that we need, if folder are present. + - test -d opencti-platform/opencti-front/test-results && tar -czvf frontend-test-results-$DRONE_BUILD_NUMBER.tar.gz opencti-platform/opencti-front/test-results + - test -d opencti-platform/opencti-front/test-results && jf rt u frontend-test-results-$DRONE_BUILD_NUMBER.tar.gz $JFROG_REPOSITORY --build-name=$JFROG_BUILD_NAME --build-number=$DRONE_BUILD_NUMBER --url=$JFROG_URL --access-token=$JFROG_TOKEN + - test -d opencti-platform/opencti-graphql/test-results && tar -czvf backend-test-results-$DRONE_BUILD_NUMBER.tar.gz opencti-platform/opencti-graphql/test-results + - test -d opencti-platform/opencti-graphql/test-results && jf rt u backend-test-results-$DRONE_BUILD_NUMBER.tar.gz $JFROG_REPOSITORY --build-name=$JFROG_BUILD_NAME --build-number=$DRONE_BUILD_NUMBER --url=$JFROG_URL --access-token=$JFROG_TOKEN # Next line should be done only once at the end: it's recording and gathering build info - jf rt bp $JFROG_BUILD_NAME $DRONE_BUILD_NUMBER --url=$JFROG_URL --access-token=$JFROG_TOKEN --build-url=$DRONE_BUILD_LINK # Cleaning up old build in JFrog diff --git a/opencti-platform/opencti-front/package.json b/opencti-platform/opencti-front/package.json index d38403a73cc3..cd1b74081ee7 100644 --- a/opencti-platform/opencti-front/package.json +++ b/opencti-platform/opencti-front/package.json @@ -36,13 +36,13 @@ "dompurify": "3.2.1", "extract-files": "13.0.0", "filigran-icon": "0.11.0", - "filigran-ui": "0.25.2", + "filigran-ui": "0.25.3", "formik": "2.4.6", "formik-mui": "5.0.0-alpha.0", "formik-mui-lab": "1.0.0", "graphiql": "3.8.3", "graphql": "16.9.0", - "graphql-ws": "5.16.1", + "graphql-ws": "5.16.2", "html-react-parser": "5.2.2", "html-to-image": "1.11.11", "html-to-pdfmake": "2.5.13", @@ -56,7 +56,7 @@ "marked": "15.0.3", "mdi-material-ui": "7.9.1", "moment": "2.30.1", - "moment-timezone": "0.5.45", + "moment-timezone": "0.5.46", "pdfmake": "0.2.13", "prop-types": "15.8.1", "qrcode": "1.5.4", @@ -65,7 +65,7 @@ "react-apexcharts": "1.7.0", "react-color": "2.19.3", "react-compound-slider": "3.4.0", - "react-cookie": "7.2.0", + "react-cookie": "7.2.2", "react-csv": "2.2.2", "react-dom": "18.3.1", "react-draggable": "4.4.6", @@ -74,7 +74,7 @@ "react-grid-layout": "1.5.0", "react-intl": "6.8.9", "react-leaflet": "4.2.1", - "react-markdown": "9.0.1", + "react-markdown": "9.0.3", "react-material-ui-carousel": "3.4.2", "react-mde": "11.5.0", "react-otp-input": "3.1.1", @@ -82,7 +82,7 @@ "react-rectangle-selection": "1.0.4", "react-relay": "17.0.0", "react-relay-network-modern": "6.2.2", - "react-router-dom": "6.28.0", + "react-router-dom": "6.28.2", "react-syntax-highlighter": "15.6.1", "react-virtualized": "9.22.5", "react-wordcloud": "1.2.7", @@ -93,8 +93,7 @@ "remark-gfm": "4.0.0", "remark-parse": "11.0.0", "rxjs": "7.8.1", - "three-spritetext": "1.9.3", - "unified": "11.0.4", + "three-spritetext": "1.9.4", "use-analytics": "1.1.0", "uuid": "11.0.3", "yup": "1.6.1" @@ -105,7 +104,7 @@ "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.2.0", - "@testing-library/user-event": "14.6.0", + "@testing-library/user-event": "14.6.1", "@types/d3-scale": "4.0.8", "@types/html-to-pdfmake": "2.4.4", "@types/node": "20.17.14", @@ -136,18 +135,18 @@ "eslint-plugin-custom-rules": "link:packages/eslint-plugin-custom-rules", "eslint-plugin-import": "2.31.0", "eslint-plugin-import-newlines": "1.4.0", - "eslint-plugin-react": "7.37.3", - "express": "4.21.0", + "eslint-plugin-react": "7.37.4", + "express": "4.21.2", "fs-extra": "11.3.0", "http-proxy-middleware": "3.0.3", - "i18n-auto-translation": "1.10.0", + "i18n-auto-translation": "1.10.1", "jsdom": "25.0.1", "monocart-reporter": "2.4.5", "react-test-renderer": "18.3.1", "relay-compiler": "17.0.0", "relay-test-utils": "17.0.0", - "typescript": "5.7.2", - "vite": "5.4.8", + "typescript": "5.7.3", + "vite": "5.4.12", "vite-plugin-relay": "2.1.0", "vite-plugin-static-copy": "2.2.0", "vitest": "2.0.5" diff --git a/opencti-platform/opencti-front/src/components/MarkdownDisplay.tsx b/opencti-platform/opencti-front/src/components/MarkdownDisplay.tsx index c304faddf1b0..f5d469958195 100644 --- a/opencti-platform/opencti-front/src/components/MarkdownDisplay.tsx +++ b/opencti-platform/opencti-front/src/components/MarkdownDisplay.tsx @@ -1,8 +1,7 @@ -import Markdown from 'react-markdown'; +import Markdown, { Options as MarkdownProps } from 'react-markdown'; import remarkParse from 'remark-parse'; import remarkFlexibleMarkers from 'remark-flexible-markers'; import { useTheme } from '@mui/styles'; -import { PluggableList } from 'react-markdown/lib'; import React, { FunctionComponent, SyntheticEvent, useState } from 'react'; import remarkGfm from 'remark-gfm'; import type { Theme } from './Theme'; @@ -55,7 +54,7 @@ interface MarkdownWithRedirectionWarningProps { commonmark?: boolean; removeLinks?: boolean; removeLineBreaks?: boolean; - remarkPlugins?: PluggableList; + remarkPlugins?: MarkdownProps['remarkPlugins']; emptyStringIfUndefined?: boolean; disableWarningAtLinkClick?: boolean; } @@ -119,13 +118,11 @@ MarkdownWithRedirectionWarningProps return ( diff --git a/opencti-platform/opencti-front/src/components/list_lines/ListLines.jsx b/opencti-platform/opencti-front/src/components/list_lines/ListLines.jsx index 2ed556175436..6f52eeb68a54 100644 --- a/opencti-platform/opencti-front/src/components/list_lines/ListLines.jsx +++ b/opencti-platform/opencti-front/src/components/list_lines/ListLines.jsx @@ -276,7 +276,7 @@ class ListLines extends Component { { if (value && value === 'export') { @@ -311,7 +311,7 @@ class ListLines extends Component { )} - {enableEntitiesView && ( + {(enableEntitiesView || (!enableEntitiesView && currentView === 'entities') || currentView === 'relationships') && ( )} - {typeof handleChangeView === 'function' - && !enableEntitiesView && ( - - - - - + {typeof handleChangeView === 'function' && !enableEntitiesView && currentView !== 'relationships' && currentView !== 'entities' && ( + + + + + )} {typeof handleChangeView === 'function' && enableGraph && ( diff --git a/opencti-platform/opencti-front/src/private/components/analyses/groupings/GroupingCreation.tsx b/opencti-platform/opencti-front/src/private/components/analyses/groupings/GroupingCreation.tsx index 0687a5482bc8..833d25ea6092 100644 --- a/opencti-platform/opencti-front/src/private/components/analyses/groupings/GroupingCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/analyses/groupings/GroupingCreation.tsx @@ -240,7 +240,7 @@ export const GroupingCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} askAi={true} /> @@ -275,26 +275,26 @@ MalwareAnalysisFormProps @@ -305,7 +305,7 @@ MalwareAnalysisFormProps required={mandatoryAttributes.includes('configuration_version')} label={t_i18n('Configuration version')} fullWidth - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> @@ -323,7 +323,7 @@ MalwareAnalysisFormProps textFieldProps={{ label: t_i18n('Analysis ended'), fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, required: mandatoryAttributes.includes('analysis_ended'), }} /> @@ -333,7 +333,7 @@ MalwareAnalysisFormProps required={mandatoryAttributes.includes('analysis_engine_version')} label={t_i18n('Analysis engine version')} fullWidth - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} />
} /> - + ); diff --git a/opencti-platform/opencti-front/src/private/components/data/ingestionRss/IngestionRssLine.jsx b/opencti-platform/opencti-front/src/private/components/data/ingestionRss/IngestionRssLine.jsx index 32ed3ee39bf8..d926770989fb 100644 --- a/opencti-platform/opencti-front/src/private/components/data/ingestionRss/IngestionRssLine.jsx +++ b/opencti-platform/opencti-front/src/private/components/data/ingestionRss/IngestionRssLine.jsx @@ -99,11 +99,17 @@ class IngestionRssLineLineComponent extends Component { status={!!node.ingestion_running} /> +
+ {nsdt(node.last_execution_date) || '-'} +
- {nsdt(node.current_state_date)} + {nsdt(node.current_state_date) || '-'}
} @@ -141,6 +147,7 @@ const IngestionRssLineFragment = createFragmentContainer( uri ingestion_running current_state_date + last_execution_date } `, }, @@ -200,6 +207,17 @@ class IngestionRssDummyComponent extends Component { height="100%" /> +
+ +
- + ); diff --git a/opencti-platform/opencti-front/src/private/components/entities/events/EventCreation.tsx b/opencti-platform/opencti-front/src/private/components/entities/events/EventCreation.tsx index f0676304e517..3b126e75a4bb 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/events/EventCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/entities/events/EventCreation.tsx @@ -242,7 +242,7 @@ export const EventCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows={4} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ label: t_i18n('Start date'), variant: 'standard', fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, }} /> = ({ label: t_i18n('End date'), variant: 'standard', fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, }} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="source" label={t_i18n('Source')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> @@ -191,7 +191,7 @@ ObservedDataFormProps label: t_i18n('First observed'), variant: 'standard', fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, }} /> {t_i18n('Reverse')} diff --git a/opencti-platform/opencti-front/src/private/components/locations/administrative_areas/AdministrativeAreaCreation.tsx b/opencti-platform/opencti-front/src/private/components/locations/administrative_areas/AdministrativeAreaCreation.tsx index a1d73b1c8a84..152535103489 100644 --- a/opencti-platform/opencti-front/src/private/components/locations/administrative_areas/AdministrativeAreaCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/locations/administrative_areas/AdministrativeAreaCreation.tsx @@ -235,7 +235,7 @@ export const AdministrativeAreaCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows={4} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="latitude" label={t_i18n('Latitude')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> { stixCoreObjectTypes={['Country']} entityLink={link} isRelationReversed={false} + enableEntitiesView={false} /> } /> @@ -148,6 +149,7 @@ const CityKnowledge = ({ cityData }: { cityData: CityKnowledge_city$key }) => { stixCoreObjectTypes={['Region']} entityLink={link} isRelationReversed={false} + enableEntitiesView={false} /> } /> diff --git a/opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx b/opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx index b4032fff59e5..eb366dea554e 100644 --- a/opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx @@ -217,7 +217,7 @@ export const CountryCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ /> = ({ /> = ({ fullWidth={true} multiline={true} rows={4} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="latitude" label={t_i18n('Latitude')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="longitude" label={t_i18n('Longitude')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="street_address" label={t_i18n('Street address')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ name="postal_code" label={t_i18n('Postal code')} fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ /> = ({ /> createStyles({ menuItem: { paddingRight: 2, height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuHoverItem: { height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuSubItem: { height: 25, - fontWeight: 400, + fontWeight: 500, fontSize: 12, }, menuSubItemWithIcon: { paddingLeft: 20, height: 25, - fontWeight: 400, + fontWeight: 500, fontSize: 12, }, menuItemText: { padding: '1px 0 0 8px', - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuSubItemText: { @@ -162,39 +162,39 @@ const useStyles = makeStyles((theme) => createStyles({ overflow: 'hidden', textOverflow: 'ellipsis', padding: '1px 0 0 8px', - fontWeight: 400, + fontWeight: 500, fontSize: 12, }, menuSubItemTextWithoutIcon: { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', - padding: '1px 0 0 16px', - fontWeight: 400, + padding: '1px 0 0 0', + fontWeight: 500, fontSize: 12, }, menuCollapseOpen: { width: OPEN_BAR_WIDTH, height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuCollapse: { width: SMALL_BAR_WIDTH, height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuLogoOpen: { width: OPEN_BAR_WIDTH, height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuLogo: { width: SMALL_BAR_WIDTH, height: 35, - fontWeight: 400, + fontWeight: 500, fontSize: 14, }, menuItemSmallText: { diff --git a/opencti-platform/opencti-front/src/private/components/observations/indicators/IndicatorCreation.tsx b/opencti-platform/opencti-front/src/private/components/observations/indicators/IndicatorCreation.tsx index 8021c5490703..c3e55c652fc7 100644 --- a/opencti-platform/opencti-front/src/private/components/observations/indicators/IndicatorCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/observations/indicators/IndicatorCreation.tsx @@ -273,7 +273,7 @@ export const IndicatorCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} detectDuplicate={['Indicator']} /> = ({ label: t_i18n('Valid from'), variant: 'standard', fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, }} /> = ({ label: t_i18n('Valid until'), variant: 'standard', fullWidth: true, - style: { marginTop: 20 }, + style: { ...fieldSpacingContainerStyle }, }} /> = ({ label={t_i18n('Score')} type="number" fullWidth={true} - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> diff --git a/opencti-platform/opencti-front/src/private/components/techniques/data_sources/DataSourceCreation.tsx b/opencti-platform/opencti-front/src/private/components/techniques/data_sources/DataSourceCreation.tsx index b5fb164f2b46..a1f5b01e32bd 100644 --- a/opencti-platform/opencti-front/src/private/components/techniques/data_sources/DataSourceCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/techniques/data_sources/DataSourceCreation.tsx @@ -240,39 +240,27 @@ export const DataSourceCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} /> diff --git a/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignCreation.tsx b/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignCreation.tsx index 04c348c22593..756e92b94260 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignCreation.tsx @@ -227,7 +227,7 @@ export const CampaignCreationForm: FunctionComponent = ({ fullWidth={true} multiline={true} rows="4" - style={{ marginTop: 20 }} + style={fieldSpacingContainerStyle} askAi={true} /> ({ - paper: { - marginTop: theme.spacing(1), - padding: '15px', - borderRadius: 4, - }, - chip: { - fontSize: 12, - lineHeight: '12px', - backgroundColor: theme.palette.background.accent, - borderRadius: 4, - color: theme.palette.text.primary, - textTransform: 'uppercase', - margin: '0 5px 5px 0', - }, - smallPre: { - display: 'inline-block', - margin: 0, - paddingTop: '7px', - paddingBottom: '4px', - }, -}); - -class IntrusionSetDetailsComponent extends Component { - render() { - const { t, classes, intrusionSet, fldt } = this.props; - const hasImages = (intrusionSet.images?.edges ?? []).filter( - (n) => n?.node?.metaData?.inCarousel, - ).length > 0; - return ( -
- - {t('Details')} - - - - - - {hasImages && ( - - - - )} - - - {t('Description')} - - - - - - - - - {t('First seen')} - - {fldt(intrusionSet.first_seen)} - - {t('Last seen')} - - {fldt(intrusionSet.last_seen)} - - - - - - {t('Resource level')} - - - - - - {t('Primary motivation')} - - - - - - {t('Goals')} - - - {intrusionSet.goals && ( - - {intrusionSet.goals.map((goal) => ( - - - - - {goal} - } - /> - - ))} - - )} - - - - - {t('Secondary motivations')} - - {intrusionSet.secondary_motivations && ( - - {intrusionSet.secondary_motivations.map( - (secondaryMotivation) => ( - - - - - - } - /> - - ), - )} - - )} - - - -
- ); - } -} - -IntrusionSetDetailsComponent.propTypes = { - intrusionSet: PropTypes.object, - classes: PropTypes.object, - t: PropTypes.func, - fd: PropTypes.func, -}; - -const IntrusionSetDetails = createFragmentContainer( - IntrusionSetDetailsComponent, - { - intrusionSet: graphql` - fragment IntrusionSetDetails_intrusionSet on IntrusionSet { - id - first_seen - last_seen - description - resource_level - primary_motivation - secondary_motivations - goals - images: importFiles(prefixMimeType: "image/") { - edges { - node { - id - name - metaData { - mimetype - order - inCarousel - description - } - } - } - } - ...IntrusionSetLocations_intrusionSet - } - `, - }, -); - -export default compose(inject18n, withStyles(styles))(IntrusionSetDetails); diff --git a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetDetails.tsx b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetDetails.tsx new file mode 100644 index 000000000000..39dbb6b76e69 --- /dev/null +++ b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetDetails.tsx @@ -0,0 +1,226 @@ +import React from 'react'; +import { graphql, createFragmentContainer } from 'react-relay'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import Grid from '@mui/material/Grid'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import { BullseyeArrow, ArmFlexOutline } from 'mdi-material-ui'; +import ListItemText from '@mui/material/ListItemText'; +import { useTheme } from '@mui/material/styles'; +import { IntrusionSetDetails_intrusionSet$data } from '@components/threats/intrusion_sets/__generated__/IntrusionSetDetails_intrusionSet.graphql'; +import ExpandableMarkdown from '../../../../components/ExpandableMarkdown'; +import { useFormatter } from '../../../../components/i18n'; +import IntrusionSetLocations from './IntrusionSetLocations'; +import ItemOpenVocab from '../../../../components/ItemOpenVocab'; +import ImageCarousel, { ImagesData } from '../../../../components/ImageCarousel'; +import FieldOrEmpty from '../../../../components/FieldOrEmpty'; + +type IntrusionSetDetailsProps = { + intrusionSet: IntrusionSetDetails_intrusionSet$data +}; + +const IntrusionSetDetailsComponent = ({ intrusionSet }: IntrusionSetDetailsProps) => { + const { t_i18n, fldt } = useFormatter(); + const theme = useTheme(); + + const imagesCarousel: { images: ImagesData } = { + images: { + edges: (intrusionSet.images?.edges ?? []).filter((n) => n?.node?.metaData?.inCarousel), + } as ImagesData, + }; + + const hasImages = intrusionSet.images?.edges && intrusionSet.images.edges.filter((n) => ( + n?.node?.metaData?.inCarousel + )).length > 0; + + return ( +
+ + {t_i18n('Details')} + + + + + + {hasImages && ( + + + + )} + + + {t_i18n('Description')} + + + + + + + + + {t_i18n('First seen')} + + {fldt(intrusionSet.first_seen)} + + {t_i18n('Last seen')} + + {fldt(intrusionSet.last_seen)} + + + + + + {t_i18n('Resource level')} + + + + + + {t_i18n('Primary motivation')} + + + + + + {t_i18n('Goals')} + + + {intrusionSet.goals && ( + + {intrusionSet.goals.map((goal) => ( + + + + + + {goal} + + } + /> + + ))} + + )} + + + + + {t_i18n('Secondary motivations')} + + {intrusionSet.secondary_motivations && ( + + {intrusionSet.secondary_motivations.map( + (secondaryMotivation) => ( + + + + + + } + /> + + ), + )} + + )} + + + +
+ ); +}; + +const IntrusionSetDetails = createFragmentContainer( + IntrusionSetDetailsComponent, + { + intrusionSet: graphql` + fragment IntrusionSetDetails_intrusionSet on IntrusionSet { + id + first_seen + last_seen + description + resource_level + primary_motivation + secondary_motivations + goals + images: importFiles(prefixMimeType: "image/") { + edges { + node { + id + name + metaData { + mimetype + order + inCarousel + description + } + } + } + } + ...IntrusionSetLocations_intrusionSet + } + `, + }, +); + +export default IntrusionSetDetails; diff --git a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/ThreatActorGroupCreation.tsx b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/ThreatActorGroupCreation.tsx index 54d8e8ff6835..0a248d3206e4 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/ThreatActorGroupCreation.tsx +++ b/opencti-platform/opencti-front/src/private/components/threats/threat_actors_group/ThreatActorGroupCreation.tsx @@ -226,12 +226,12 @@ ThreatActorGroupFormProps name="threat_actor_types" label={t_i18n('Threat actor types')} multiple={true} - containerStyle={{ width: '100%', marginTop: 20 }} + containerStyle={fieldSpacingContainerStyle} onChange={setFieldValue} /> =7.21.4" - checksum: 10/01a7481642ceda10324ff5356e3cfd9c6131b0cecbcbdd5938096d4d3f8ce9e548e9b460ef35bad8f3649dc392c808044a5abd78de8218a4bc21c91125be85df + checksum: 10/34b74fff56a0447731a94b40d4cf246deb8dbc1c1e3aec93acd1c3377a760bb062e979f1572bb34ec164ad28ee2a391744b42d0d6d6cc16c4ce527e5e09610e1 languageName: node linkType: hard @@ -11403,9 +11403,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:7.37.3": - version: 7.37.3 - resolution: "eslint-plugin-react@npm:7.37.3" +"eslint-plugin-react@npm:7.37.4": + version: 7.37.4 + resolution: "eslint-plugin-react@npm:7.37.4" dependencies: array-includes: "npm:^3.1.8" array.prototype.findlast: "npm:^1.2.5" @@ -11427,7 +11427,7 @@ __metadata: string.prototype.repeat: "npm:^1.0.0" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - checksum: 10/30042b95c440a962157192f67369d8c9171f7c73e992695e5674c9d28c3cfe4098488eca86dfa7c433d4c8a91243cbafdc80c7e42d2c1720a427ddb36e65457e + checksum: 10/c538c10665c87cb90a0bcc4efe53a758570db10997d079d31474a9760116ef5584648fa22403d889ca672df8071bda10b40434ea0499e5ee8360bc5c8aba1679 languageName: node linkType: hard @@ -11731,16 +11731,16 @@ __metadata: languageName: node linkType: hard -"express@npm:4.21.0": - version: 4.21.0 - resolution: "express@npm:4.21.0" +"express@npm:4.21.2": + version: 4.21.2 + resolution: "express@npm:4.21.2" dependencies: accepts: "npm:~1.3.8" array-flatten: "npm:1.1.1" body-parser: "npm:1.20.3" content-disposition: "npm:0.5.4" content-type: "npm:~1.0.4" - cookie: "npm:0.6.0" + cookie: "npm:0.7.1" cookie-signature: "npm:1.0.6" debug: "npm:2.6.9" depd: "npm:2.0.0" @@ -11754,7 +11754,7 @@ __metadata: methods: "npm:~1.1.2" on-finished: "npm:2.4.1" parseurl: "npm:~1.3.3" - path-to-regexp: "npm:0.1.10" + path-to-regexp: "npm:0.1.12" proxy-addr: "npm:~2.0.7" qs: "npm:6.13.0" range-parser: "npm:~1.2.1" @@ -11766,7 +11766,7 @@ __metadata: type-is: "npm:~1.6.18" utils-merge: "npm:1.0.1" vary: "npm:~1.1.2" - checksum: 10/3b1ee5bc5b1bd996f688702519cebc9b63a24e506965f6e1773268238cfa2c24ffdb38cc3fcb4fde66f77de1c0bebd9ee058dad06bb9c6f084b525f3c09164d3 + checksum: 10/34571c442fc8c9f2c4b442d2faa10ea1175cf8559237fc6a278f5ce6254a8ffdbeb9a15d99f77c1a9f2926ab183e3b7ba560e3261f1ad4149799e3412ab66bd1 languageName: node linkType: hard @@ -11948,9 +11948,9 @@ __metadata: languageName: node linkType: hard -"filigran-ui@npm:0.25.2": - version: 0.25.2 - resolution: "filigran-ui@npm:0.25.2" +"filigran-ui@npm:0.25.3": + version: 0.25.3 + resolution: "filigran-ui@npm:0.25.3" dependencies: "@dnd-kit/core": "npm:6.1.0" "@dnd-kit/modifiers": "npm:7.0.0" @@ -11982,7 +11982,7 @@ __metadata: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 || ^19.0 react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 - checksum: 10/dacdeb9d8484c27f811df1f74b36891c58c2b2a6ecd47435c30a2fbba36c9f20878ddc4692f8a6183d0993ac6462129d099cf614db29f9bbad0712fd35936df9 + checksum: 10/5a4f9e4f4450b0a8dfd3d0618a7d9a75cb30f25281e4128af687570f6039dc9ce3273739cdc063024adaf3f6c131a300f8a1fa646645a32e8bb68ec72d4a9f2d languageName: node linkType: hard @@ -12610,9 +12610,9 @@ __metadata: languageName: node linkType: hard -"glob@npm:11.0.0": - version: 11.0.0 - resolution: "glob@npm:11.0.0" +"glob@npm:11.0.1": + version: 11.0.1 + resolution: "glob@npm:11.0.1" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^4.0.1" @@ -12622,7 +12622,7 @@ __metadata: path-scurry: "npm:^2.0.0" bin: glob: dist/esm/bin.mjs - checksum: 10/e66939201d11ae30fe97e3364ac2be5c59d6c9bfce18ac633edfad473eb6b46a7553f6f73658f67caaf6cccc1df1ae336298a45e9021fa5695fd78754cc1603e + checksum: 10/57b12a05cc25f1c38f3b24cf6ea7a8bacef11e782c4b9a8c5b0bef3e6c5bcb8c4548cb31eb4115592e0490a024c1bde7359c470565608dd061d3b21179740457 languageName: node linkType: hard @@ -12808,12 +12808,12 @@ __metadata: languageName: node linkType: hard -"graphql-ws@npm:5.16.1": - version: 5.16.1 - resolution: "graphql-ws@npm:5.16.1" +"graphql-ws@npm:5.16.2": + version: 5.16.2 + resolution: "graphql-ws@npm:5.16.2" peerDependencies: graphql: ">=0.11 <=16" - checksum: 10/ce82ddcb1eb705ea87dac3b454562de9abaec04beef0a3726ffaaa5e6c3cd478217c82d7a9d07902c936c5567db50a0a4d3c95c4274d1b088aeaacb03833f0a7 + checksum: 10/6647bfe640b467f27aaf5ee044c1d114fe266e82cda4ebbb4368d5a4e98df5d2de9d6be70d28eb5e821d87fbf8964c3a8a18abf87c76d4f148800fd8e0488c3d languageName: node linkType: hard @@ -13254,20 +13254,20 @@ __metadata: languageName: node linkType: hard -"i18n-auto-translation@npm:1.10.0": - version: 1.10.0 - resolution: "i18n-auto-translation@npm:1.10.0" +"i18n-auto-translation@npm:1.10.1": + version: 1.10.1 + resolution: "i18n-auto-translation@npm:1.10.1" dependencies: "@google-cloud/translate": "npm:8.5.0" axios: "npm:1.7.9" deep-object-diff: "npm:1.1.9" - glob: "npm:11.0.0" + glob: "npm:11.0.1" html-entities: "npm:2.5.2" just-extend: "npm:6.2.0" yargs: "npm:17.7.2" bin: i18n-auto-translation: dist/index.js - checksum: 10/2ba374991ad00b72ad61e5d9453eeabb933a50b0cdb5b975f5b6aae5734be9e118b706068dfc0329e3c7f46467b69242f4841cf16df8f6f31fe55682ce9e6bb8 + checksum: 10/1157f6a12446da3f04a484d7acadbb0817df9bbad5925c51933d468161018efef03b6da68d4cfd2b6d29ae7a7d4bc8f7f299d081431c497c5e3cc61d4c0101ef languageName: node linkType: hard @@ -15979,12 +15979,12 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:0.5.45": - version: 0.5.45 - resolution: "moment-timezone@npm:0.5.45" +"moment-timezone@npm:0.5.46": + version: 0.5.46 + resolution: "moment-timezone@npm:0.5.46" dependencies: moment: "npm:^2.29.4" - checksum: 10/45e3793d44bea8e826c934a335ebf0b92c6d6dae562fdb59d8c45a16d5c11de4d6692b5fa7eebca969881f06b81b55f8535883bfbc727b597d601709fa5a2bb2 + checksum: 10/7613ba388fa6004af62675fb9945cb0d37758b559d07470a5e188419ffe1ac03eb2ed16fe80aa34d1e7dd39fc5bd67dc02cd59e8dcdab95504cface2c78e4b3d languageName: node linkType: hard @@ -16547,7 +16547,7 @@ __metadata: "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.3" "@testing-library/react": "npm:16.2.0" - "@testing-library/user-event": "npm:14.6.0" + "@testing-library/user-event": "npm:14.6.1" "@types/d3-scale": "npm:4.0.8" "@types/html-to-pdfmake": "npm:2.4.4" "@types/node": "npm:20.17.14" @@ -16591,23 +16591,23 @@ __metadata: eslint-plugin-custom-rules: "link:packages/eslint-plugin-custom-rules" eslint-plugin-import: "npm:2.31.0" eslint-plugin-import-newlines: "npm:1.4.0" - eslint-plugin-react: "npm:7.37.3" - express: "npm:4.21.0" + eslint-plugin-react: "npm:7.37.4" + express: "npm:4.21.2" extract-files: "npm:13.0.0" filigran-icon: "npm:0.11.0" - filigran-ui: "npm:0.25.2" + filigran-ui: "npm:0.25.3" formik: "npm:2.4.6" formik-mui: "npm:5.0.0-alpha.0" formik-mui-lab: "npm:1.0.0" fs-extra: "npm:11.3.0" graphiql: "npm:3.8.3" graphql: "npm:16.9.0" - graphql-ws: "npm:5.16.1" + graphql-ws: "npm:5.16.2" html-react-parser: "npm:5.2.2" html-to-image: "npm:1.11.11" html-to-pdfmake: "npm:2.5.13" http-proxy-middleware: "npm:3.0.3" - i18n-auto-translation: "npm:1.10.0" + i18n-auto-translation: "npm:1.10.1" invert-color: "npm:2.0.0" js-base64: "npm:3.7.7" js-file-download: "npm:0.4.12" @@ -16619,7 +16619,7 @@ __metadata: marked: "npm:15.0.3" mdi-material-ui: "npm:7.9.1" moment: "npm:2.30.1" - moment-timezone: "npm:0.5.45" + moment-timezone: "npm:0.5.46" monocart-reporter: "npm:2.4.5" pdfmake: "npm:0.2.13" prop-types: "npm:15.8.1" @@ -16629,7 +16629,7 @@ __metadata: react-apexcharts: "npm:1.7.0" react-color: "npm:2.19.3" react-compound-slider: "npm:3.4.0" - react-cookie: "npm:7.2.0" + react-cookie: "npm:7.2.2" react-csv: "npm:2.2.2" react-dom: "npm:18.3.1" react-draggable: "npm:4.4.6" @@ -16638,7 +16638,7 @@ __metadata: react-grid-layout: "npm:1.5.0" react-intl: "npm:6.8.9" react-leaflet: "npm:4.2.1" - react-markdown: "npm:9.0.1" + react-markdown: "npm:9.0.3" react-material-ui-carousel: "npm:3.4.2" react-mde: "npm:11.5.0" react-otp-input: "npm:3.1.1" @@ -16646,7 +16646,7 @@ __metadata: react-rectangle-selection: "npm:1.0.4" react-relay: "npm:17.0.0" react-relay-network-modern: "npm:6.2.2" - react-router-dom: "npm:6.28.0" + react-router-dom: "npm:6.28.2" react-syntax-highlighter: "npm:15.6.1" react-test-renderer: "npm:18.3.1" react-virtualized: "npm:9.22.5" @@ -16660,12 +16660,11 @@ __metadata: remark-gfm: "npm:4.0.0" remark-parse: "npm:11.0.0" rxjs: "npm:7.8.1" - three-spritetext: "npm:1.9.3" - typescript: "npm:5.7.2" - unified: "npm:11.0.4" + three-spritetext: "npm:1.9.4" + typescript: "npm:5.7.3" use-analytics: "npm:1.1.0" uuid: "npm:11.0.3" - vite: "npm:5.4.8" + vite: "npm:5.4.12" vite-plugin-relay: "npm:2.1.0" vite-plugin-static-copy: "npm:2.2.0" vitest: "npm:2.0.5" @@ -16926,10 +16925,10 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:0.1.10": - version: 0.1.10 - resolution: "path-to-regexp@npm:0.1.10" - checksum: 10/894e31f1b20e592732a87db61fff5b95c892a3fe430f9ab18455ebe69ee88ef86f8eb49912e261f9926fc53da9f93b46521523e33aefd9cb0a7b0d85d7096006 +"path-to-regexp@npm:0.1.12": + version: 0.1.12 + resolution: "path-to-regexp@npm:0.1.12" + checksum: 10/2e30f6a0144679c1f95c98e166b96e6acd1e72be9417830fefc8de7ac1992147eb9a4c7acaa59119fb1b3c34eec393b2129ef27e24b2054a3906fc4fb0d1398e languageName: node linkType: hard @@ -17450,16 +17449,16 @@ __metadata: languageName: node linkType: hard -"react-cookie@npm:7.2.0": - version: 7.2.0 - resolution: "react-cookie@npm:7.2.0" +"react-cookie@npm:7.2.2": + version: 7.2.2 + resolution: "react-cookie@npm:7.2.2" dependencies: "@types/hoist-non-react-statics": "npm:^3.3.5" hoist-non-react-statics: "npm:^3.3.2" universal-cookie: "npm:^7.0.0" peerDependencies: react: ">= 16.3.0" - checksum: 10/da0ea3911307fbedb8031670fa99784764d0a5c027c746d0acb12499d4668f989788395a13975f3542050f75764881bee9d4914518adaf31e2f72fe24c042a53 + checksum: 10/f83da8d96a6602fcb37213921c678c8dcfa22c8c763dd3f9cea0a5e6e570490f37a1a39343a301ce11a645886c2a7fdd3258d58018d4dcc4d90935ddff038579 languageName: node linkType: hard @@ -17662,9 +17661,9 @@ __metadata: languageName: node linkType: hard -"react-markdown@npm:9.0.1": - version: 9.0.1 - resolution: "react-markdown@npm:9.0.1" +"react-markdown@npm:9.0.3": + version: 9.0.3 + resolution: "react-markdown@npm:9.0.3" dependencies: "@types/hast": "npm:^3.0.0" devlop: "npm:^1.0.0" @@ -17679,7 +17678,7 @@ __metadata: peerDependencies: "@types/react": ">=18" react: ">=18" - checksum: 10/71ce31f200982f641d363888a26e8fb52a199a589124f20295e9be870fa3aed26fcfa14d1dc766d83df666a15cb82359291bfda207bd55d5728ff376d217e079 + checksum: 10/b97eb9a61762762043263286ece030bd878acabe24bbf433767a9518cb18e434e26a75b1b810a7cb966e304ddb4e16bd4a15edcc808113b11b4fb85a68d99e8d languageName: node linkType: hard @@ -17933,27 +17932,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:6.28.0": - version: 6.28.0 - resolution: "react-router-dom@npm:6.28.0" +"react-router-dom@npm:6.28.2": + version: 6.28.2 + resolution: "react-router-dom@npm:6.28.2" dependencies: - "@remix-run/router": "npm:1.21.0" - react-router: "npm:6.28.0" + "@remix-run/router": "npm:1.21.1" + react-router: "npm:6.28.2" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 10/e637825132ea96c3514ef7b8322f9bf0b752a942d6b4ffc4c20e389b5911726adf3dba8208ed4b97bf5b9c3bd465d9d1a1db1a58a610a8d528f18d890e0b143f + checksum: 10/4775cb484c497be5833ef5e048378d685f970a72c75a595c8b74fce147eda9e705d885b71d888b1090a8f22e7630adb851766d34e771c3a649f73171f0fa4c2b languageName: node linkType: hard -"react-router@npm:6.28.0": - version: 6.28.0 - resolution: "react-router@npm:6.28.0" +"react-router@npm:6.28.2": + version: 6.28.2 + resolution: "react-router@npm:6.28.2" dependencies: - "@remix-run/router": "npm:1.21.0" + "@remix-run/router": "npm:1.21.1" peerDependencies: react: ">=16.8" - checksum: 10/f021a644513144884a567d9c2dcc432e8e3233f931378c219c5a3b5b842340f0faca86225a708bafca1e9010965afe1a7dada28aef5b7b6138c885c0552d9a7d + checksum: 10/4cf150e3762acff8a087d6b474861fdb73efdf829ce0619bc980f3d8fc5d9e45e67333ab7d62af5b775fba8efe8f8d342f089bec75f9b41f3162e139c0187efd languageName: node linkType: hard @@ -19951,12 +19950,12 @@ __metadata: languageName: node linkType: hard -"three-spritetext@npm:1.9.3": - version: 1.9.3 - resolution: "three-spritetext@npm:1.9.3" +"three-spritetext@npm:1.9.4": + version: 1.9.4 + resolution: "three-spritetext@npm:1.9.4" peerDependencies: three: ">=0.86.0" - checksum: 10/dcaac7272563558df067ec9680fe03cb1c714efeedeb8882579a5d1638ccf2e32bf2bc95280aaa174fb2f6c5012baf1ac190b31a1594e5948a0156b05fe7805c + checksum: 10/23fc02b16cdc4995e903fed17d01c828304a888dd01ccc7673b6774561ecaf41fcb80b267e7415d4aa104db3ad06180909b88d34f6853850c482dd65a521fd71 languageName: node linkType: hard @@ -20424,23 +20423,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.7.2": - version: 5.7.2 - resolution: "typescript@npm:5.7.2" +"typescript@npm:5.7.3": + version: 5.7.3 + resolution: "typescript@npm:5.7.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a + checksum: 10/6a7e556de91db3d34dc51cd2600e8e91f4c312acd8e52792f243c7818dfadb27bae677175fad6947f9c81efb6c57eb6b2d0c736f196a6ee2f1f7d57b74fc92fa languageName: node linkType: hard -"typescript@patch:typescript@npm%3A5.7.2#optional!builtin": - version: 5.7.2 - resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin::version=5.7.2&hash=5786d5" +"typescript@patch:typescript@npm%3A5.7.3#optional!builtin": + version: 5.7.3 + resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin::version=5.7.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/d75ca10141afc64fd3474b41a8b082b640555bed388d237558aed64e5827ddadb48f90932c7f4205883f18f5bcab8b6a739a2cfac95855604b0dfeb34bc2f3eb + checksum: 10/dc58d777eb4c01973f7fbf1fd808aad49a0efdf545528dab9b07d94fdcb65b8751742804c3057e9619a4627f2d9cc85547fdd49d9f4326992ad0181b49e61d81 languageName: node linkType: hard @@ -20547,21 +20546,6 @@ __metadata: languageName: node linkType: hard -"unified@npm:11.0.4": - version: 11.0.4 - resolution: "unified@npm:11.0.4" - dependencies: - "@types/unist": "npm:^3.0.0" - bail: "npm:^2.0.0" - devlop: "npm:^1.0.0" - extend: "npm:^3.0.0" - is-plain-obj: "npm:^4.0.0" - trough: "npm:^2.0.0" - vfile: "npm:^6.0.0" - checksum: 10/425f0618d6f5e5d2ae64ec206cb6fd11f4b86fec7a785cfe2fc3a334191a91bf837eecb32858c70bcc2c08e76ce9d6a38457319f70f77399c8f496fb8e486817 - languageName: node - linkType: hard - "unified@npm:^11.0.0": version: 11.0.5 resolution: "unified@npm:11.0.5" @@ -21045,9 +21029,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:5.4.8": - version: 5.4.8 - resolution: "vite@npm:5.4.8" +"vite@npm:5.4.12": + version: 5.4.12 + resolution: "vite@npm:5.4.12" dependencies: esbuild: "npm:^0.21.3" fsevents: "npm:~2.3.3" @@ -21084,7 +21068,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/17fdffa558abaf854f04ead7d3ddd76e4556a59871f9ac63cca3fc20a79979984837d8dddaae4b171e3d73061f781e4eec0f6d3babdbce2b4d111d29cf474c1c + checksum: 10/16a2b486164717b51f8a22db750bba1d1d5be939e28d199a27a3c3f54cea484b3f2e8618eeafaec8b5075c011aa9aafd3e86b54411ba90555617190e626fe4ef languageName: node linkType: hard diff --git a/opencti-platform/opencti-graphql/config/default.json b/opencti-platform/opencti-graphql/config/default.json index f5bd8e34d740..2927df7f7bea 100644 --- a/opencti-platform/opencti-graphql/config/default.json +++ b/opencti-platform/opencti-graphql/config/default.json @@ -266,7 +266,14 @@ "ingestion_manager": { "enabled": true, "lock_key": "ingestion_manager_lock", - "interval": 30000 + "interval": 30000, + "rss_feed": { + "min_interval_minutes": 5, + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0" + }, + "csv_feed": { + "min_interval_minutes": 5 + } }, "retention_manager": { "enabled": true, diff --git a/opencti-platform/opencti-graphql/package.json b/opencti-platform/opencti-graphql/package.json index 50271535d10e..2602855fd048 100644 --- a/opencti-platform/opencti-graphql/package.json +++ b/opencti-platform/opencti-graphql/package.json @@ -97,7 +97,7 @@ "dot-object": "2.1.5", "ejs": "3.1.10", "eventsource": "2.0.2", - "express": "4.21.0", + "express": "4.21.2", "express-rate-limit": "7.5.0", "express-session": "1.18.1", "fast-json-patch": "3.1.1", @@ -112,7 +112,7 @@ "graphql-scalars": "1.23.0", "graphql-subscriptions": "2.0.0", "graphql-upload": "17.0.0", - "graphql-ws": "5.16.1", + "graphql-ws": "5.16.2", "helmet": "7.2.0", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", @@ -122,7 +122,7 @@ "js-deep-equals": "2.1.1", "json-to-plain-text": "1.1.4", "jwt-decode": "4.0.0", - "lru-cache": "11.0.0", + "lru-cache": "11.0.2", "migrate": "2.1.0", "mime-types": "2.1.35", "moment": "2.30.1", @@ -131,7 +131,7 @@ "node-calls-python": "1.9.1", "node-fetch": "3.3.2", "node-forge": "1.3.1", - "nodemailer": "6.9.15", + "nodemailer": "6.9.16", "openai": "4.78.1", "openid-client": "5.6.5", "opentelemetry-node-metrics": "3.0.0", @@ -198,12 +198,12 @@ "eslint-plugin-import": "2.31.0", "eslint-plugin-import-newlines": "1.4.0", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-react": "7.37.3", + "eslint-plugin-react": "7.37.4", "eslint-plugin-react-hooks": "4.6.2", "fast-glob": "3.3.3", "graphql-tag": "2.12.6", - "ts-loader": "9.5.1", - "typescript": "5.7.2", + "ts-loader": "9.5.2", + "typescript": "5.7.3", "vitest": "2.0.5" }, "resolutions": { diff --git a/opencti-platform/opencti-graphql/src/database/engine.js b/opencti-platform/opencti-graphql/src/database/engine.js index 55c171f705fe..f72e53644057 100644 --- a/opencti-platform/opencti-graphql/src/database/engine.js +++ b/opencti-platform/opencti-graphql/src/database/engine.js @@ -87,15 +87,26 @@ import { ATTRIBUTE_EXPLANATION, ATTRIBUTE_NAME, ENTITY_TYPE_IDENTITY_INDIVIDUAL, + ENTITY_TYPE_IDENTITY_SECTOR, ENTITY_TYPE_IDENTITY_SYSTEM, + ENTITY_TYPE_LOCATION_CITY, ENTITY_TYPE_LOCATION_COUNTRY, + ENTITY_TYPE_LOCATION_REGION, isStixDomainObject, STIX_ORGANIZATIONS_RESTRICTED, STIX_ORGANIZATIONS_UNRESTRICTED } from '../schema/stixDomainObject'; import { isBasicObject, isStixCoreObject, isStixObject } from '../schema/stixCoreObject'; import { isBasicRelationship, isStixRelationship } from '../schema/stixRelationship'; -import { isStixCoreRelationship, RELATION_INDICATES, RELATION_PUBLISHES, RELATION_RELATED_TO, STIX_CORE_RELATIONSHIPS } from '../schema/stixCoreRelationship'; +import { + isStixCoreRelationship, + RELATION_INDICATES, + RELATION_LOCATED_AT, + RELATION_PUBLISHES, + RELATION_RELATED_TO, + RELATION_TARGETS, + STIX_CORE_RELATIONSHIPS +} from '../schema/stixCoreRelationship'; import { generateInternalId, INTERNAL_FROM_FIELD, INTERNAL_TO_FIELD } from '../schema/identifier'; import { BYPASS, @@ -176,7 +187,7 @@ import { controlUserConfidenceAgainstElement } from '../utils/confidence-level'; import { getDraftContext } from '../utils/draftContext'; import { enrichWithRemoteCredentials } from '../config/credentials'; import { ENTITY_TYPE_DRAFT_WORKSPACE } from '../modules/draftWorkspace/draftWorkspace-types'; -import { isStixCyberObservable } from '../schema/stixCyberObservable'; +import { ENTITY_IPV4_ADDR, ENTITY_IPV6_ADDR, isStixCyberObservable } from '../schema/stixCyberObservable'; import { lockResource } from './redis'; const ELK_ENGINE = 'elk'; @@ -224,10 +235,35 @@ export const UNIMPACTED_ENTITIES_ROLE = [ // RELATION_EXTERNAL_REFERENCE `${RELATION_INDICATES}_${ROLE_TO}`, ]; -export const isImpactedTypeAndSide = (type, side) => { +const LOCATED_AT_CLEANED = [ENTITY_TYPE_LOCATION_REGION, ENTITY_TYPE_LOCATION_COUNTRY]; +const UNSUPPORTED_LOCATED_AT = [ENTITY_IPV4_ADDR, ENTITY_IPV6_ADDR, ENTITY_TYPE_LOCATION_CITY]; +export const isSpecialNonImpactedCases = (relationshipType, fromType, toType, side) => { + // Rel on the "to" side with related-to from observable + if (side === ROLE_TO && relationshipType === RELATION_RELATED_TO && isStixCyberObservable(fromType)) { + return true; + } + // Rel on the "to" side with located-at from IP / cities to region / country + if (side === ROLE_TO && relationshipType === RELATION_LOCATED_AT && UNSUPPORTED_LOCATED_AT.includes(fromType) && LOCATED_AT_CLEANED.includes(toType)) { + return true; + } + // Rel on the "to" side with targets from any threat to region / country / sector + if (side === ROLE_TO && relationshipType === RELATION_TARGETS && [ENTITY_TYPE_LOCATION_REGION, ENTITY_TYPE_LOCATION_COUNTRY, ENTITY_TYPE_IDENTITY_SECTOR].includes(toType)) { + return true; + } + return false; +}; +export const isImpactedTypeAndSide = (type, fromType, toType, side) => { + if (isSpecialNonImpactedCases(type, fromType, toType, side)) { + return false; + } return !UNIMPACTED_ENTITIES_ROLE.includes(`${type}_${side}`); }; -export const isImpactedRole = (role) => !UNIMPACTED_ENTITIES_ROLE.includes(role); +export const isImpactedRole = (type, fromType, toType, role) => { + if (isSpecialNonImpactedCases(type, fromType, toType, role.split('_').at(1))) { + return false; + } + return !UNIMPACTED_ENTITIES_ROLE.includes(role); +}; let engine; let isRuntimeSortingEnable = false; @@ -3622,7 +3658,7 @@ const computeDeleteElementsImpacts = async (cleanupRelations, toBeRemovedIds, re for (let i = 0; i < cleanupRelations.length; i += 1) { const relation = cleanupRelations[i]; const fromWillNotBeRemoved = !relationsToRemoveMap.has(relation.fromId) && !toBeRemovedIds.includes(relation.fromId); - const isFromCleanup = fromWillNotBeRemoved && isImpactedTypeAndSide(relation.entity_type, ROLE_FROM); + const isFromCleanup = fromWillNotBeRemoved && isImpactedTypeAndSide(relation.entity_type, relation.fromType, relation.toType, ROLE_FROM); const cleanKey = `${relation.entity_type}|${relation._index}`; if (isFromCleanup) { if (isEmptyField(elementsImpact[relation.fromId])) { @@ -3637,7 +3673,7 @@ const computeDeleteElementsImpacts = async (cleanupRelations, toBeRemovedIds, re } } const toWillNotBeRemoved = !relationsToRemoveMap.has(relation.toId) && !toBeRemovedIds.includes(relation.toId); - const isToCleanup = toWillNotBeRemoved && isImpactedTypeAndSide(relation.entity_type, ROLE_TO); + const isToCleanup = toWillNotBeRemoved && isImpactedTypeAndSide(relation.entity_type, relation.fromType, relation.toType, ROLE_TO); if (isToCleanup) { if (isEmptyField(elementsImpact[relation.toId])) { elementsImpact[relation.toId] = { [cleanKey]: [relation.fromId] }; @@ -4052,7 +4088,7 @@ export const elIndexElements = async (context, user, indexingType, elements) => const impactedEntities = R.pipe( R.filter((e) => e.base_type === BASE_TYPE_RELATION), R.map((e) => { - const { fromType, fromRole, toRole } = e; + const { fromType, fromRole, toType, toRole } = e; const impacts = []; // We impact target entities of the relation only if not global entities like // MarkingDefinition (marking) / KillChainPhase (kill_chain_phase) / Label (tagging) @@ -4060,13 +4096,10 @@ export const elIndexElements = async (context, user, indexingType, elements) => cache[e.toId] = e.to; const refField = isStixRefRelationship(e.entity_type) && isInferredIndex(e._index) ? ID_INFERRED : ID_INTERNAL; const relationshipType = e.entity_type; - const isRelatedToFromObservable = isStixCyberObservable(fromType) && relationshipType === RELATION_RELATED_TO; - if (isImpactedRole(fromRole)) { + if (isImpactedRole(relationshipType, fromType, toType, fromRole)) { impacts.push({ refField, from: e.fromId, relationshipType, to: e.to, type: e.to.entity_type, side: 'from' }); } - // Waiting for JRI work, we need to avoid impact rel on very large entities - // Slowing down the performances due to original misconception - if (isImpactedRole(toRole) && !isRelatedToFromObservable) { + if (isImpactedRole(relationshipType, fromType, toType, toRole)) { impacts.push({ refField, from: e.toId, relationshipType, to: e.from, type: e.from.entity_type, side: 'to' }); } return impacts; diff --git a/opencti-platform/opencti-graphql/src/database/middleware.js b/opencti-platform/opencti-graphql/src/database/middleware.js index 7c4d64e67bda..fef3ea0f3381 100644 --- a/opencti-platform/opencti-graphql/src/database/middleware.js +++ b/opencti-platform/opencti-graphql/src/database/middleware.js @@ -1214,7 +1214,7 @@ const mergeEntitiesRaw = async (context, user, targetEntity, sourceEntities, tar }; updateConnections.push(relUpdate); // Update the side that will remain (RELATED_ELEMENT) - if (isImpactedTypeAndSide(entity.i_relation.entity_type, ROLE_TO)) { + if (isImpactedTypeAndSide(entity.i_relation.entity_type, entity.i_relation.fromType, entity.i_relation.toType, ROLE_TO)) { updateEntities.push({ _index: entity._index, id: sideToKeep, @@ -1225,7 +1225,7 @@ const mergeEntitiesRaw = async (context, user, targetEntity, sourceEntities, tar }); } // Update the MERGED TARGET (Need to add the relation side) - if (isImpactedTypeAndSide(entity.i_relation.entity_type, ROLE_FROM)) { + if (isImpactedTypeAndSide(entity.i_relation.entity_type, entity.i_relation.fromType, entity.i_relation.toType, ROLE_FROM)) { updateEntities.push({ _index: targetEntity._index, id: sideTarget, @@ -1256,7 +1256,7 @@ const mergeEntitiesRaw = async (context, user, targetEntity, sourceEntities, tar }; updateConnections.push(relUpdate); // Update the side that will remain (RELATED_ELEMENT) - if (isImpactedTypeAndSide(entity.i_relation.entity_type, ROLE_FROM)) { + if (isImpactedTypeAndSide(entity.i_relation.entity_type, entity.i_relation.fromType, entity.i_relation.toType, ROLE_FROM)) { updateEntities.push({ _index: entity._index, id: sideToKeep, @@ -1267,7 +1267,7 @@ const mergeEntitiesRaw = async (context, user, targetEntity, sourceEntities, tar }); } // Update the MERGED TARGET (Need to add the relation side) - if (isImpactedTypeAndSide(entity.i_relation.entity_type, ROLE_TO)) { + if (isImpactedTypeAndSide(entity.i_relation.entity_type, entity.i_relation.fromType, entity.i_relation.toType, ROLE_TO)) { updateEntities.push({ _index: targetEntity._index, id: sideTarget, @@ -2814,8 +2814,8 @@ export const createRelationRaw = async (context, user, rawInput, opts = {}) => { // Build lock ids const inputIds = getInputIds(relationshipType, resolvedInput, fromRule); - if (isImpactedTypeAndSide(relationshipType, ROLE_FROM)) inputIds.push(from.internal_id); - if (isImpactedTypeAndSide(relationshipType, ROLE_TO)) inputIds.push(to.internal_id); + if (isImpactedTypeAndSide(relationshipType, from.entity_type, to.entity_type, ROLE_FROM)) inputIds.push(from.internal_id); + if (isImpactedTypeAndSide(relationshipType, from.entity_type, to.entity_type, ROLE_TO)) inputIds.push(to.internal_id); const participantIds = inputIds.filter((e) => !locks.includes(e)); try { // Try to get the lock in redis @@ -2990,12 +2990,9 @@ const createEntityRaw = async (context, user, rawInput, type, opts = {}) => { } // region - Pre-Check const entitySetting = await getEntitySettingFromCache(context, type); - const filledInput = fillDefaultValues(user, input, entitySetting); - await validateEntityAndRelationCreation(context, user, filledInput, type, entitySetting, opts); - // endregion const { fromRule } = opts; // We need to check existing dependencies - const resolvedInput = await inputResolveRefs(context, user, filledInput, type, entitySetting); + let resolvedInput = await inputResolveRefs(context, user, input, type, entitySetting); // Generate all the possibles ids // For marking def, we need to force the standard_id const participantIds = getInputIds(type, resolvedInput, fromRule); @@ -3022,6 +3019,12 @@ const createEntityRaw = async (context, user, rawInput, type, opts = {}) => { // Resolve the existing entity const [existingByIds, existingByHashed] = await Promise.all([existingByIdsPromise, existingByHashedPromise]); existingEntities.push(...R.uniqBy((e) => e.internal_id, [...existingByIds, ...existingByHashed])); + // region - Pre-Check + if (existingEntities.length === 0) { // We do not use default values on upsert. + resolvedInput = fillDefaultValues(user, resolvedInput, entitySetting); + } + await validateEntityAndRelationCreation(context, user, resolvedInput, type, entitySetting, opts); + // endregion // If existing entities have been found and type is a STIX Core Object let dataMessage; if (existingEntities.length > 0) { @@ -3169,6 +3172,7 @@ export const createEntity = async (context, user, input, type, opts = {}) => { } return isCompleteResult ? data : data.element; }; + export const createInferredEntity = async (context, input, ruleContent, type) => { const opts = { fromRule: ruleContent.field, diff --git a/opencti-platform/opencti-graphql/src/domain/city.js b/opencti-platform/opencti-graphql/src/domain/city.js index db81ee1107de..e6185e33571c 100644 --- a/opencti-platform/opencti-graphql/src/domain/city.js +++ b/opencti-platform/opencti-graphql/src/domain/city.js @@ -3,7 +3,7 @@ import { BUS_TOPICS } from '../config/conf'; import { notify } from '../database/redis'; import { ENTITY_TYPE_LOCATION_CITY, ENTITY_TYPE_LOCATION_COUNTRY } from '../schema/stixDomainObject'; import { ABSTRACT_STIX_DOMAIN_OBJECT } from '../schema/general'; -import { listEntities, loadEntityThroughRelationsPaginated, storeLoadById } from '../database/middleware-loader'; +import { listAllToEntitiesThroughRelations, listEntities, storeLoadById } from '../database/middleware-loader'; import { RELATION_LOCATED_AT } from '../schema/stixCoreRelationship'; export const findById = (context, user, cityId) => { @@ -14,8 +14,9 @@ export const findAll = (context, user, args) => { return listEntities(context, user, [ENTITY_TYPE_LOCATION_CITY], args); }; -export const locatedAtCountry = async (context, user, stixCoreObjectId) => { - return loadEntityThroughRelationsPaginated(context, user, stixCoreObjectId, RELATION_LOCATED_AT, ENTITY_TYPE_LOCATION_COUNTRY, false); +export const locatedAtCountry = async (context, user, cityId) => { + const countries = await listAllToEntitiesThroughRelations(context, user, cityId, RELATION_LOCATED_AT, ENTITY_TYPE_LOCATION_COUNTRY); + return countries.at(0); }; export const addCity = async (context, user, city) => { diff --git a/opencti-platform/opencti-graphql/src/generated/graphql.ts b/opencti-platform/opencti-graphql/src/generated/graphql.ts index 24ec28cd6c44..a9eabe707498 100644 --- a/opencti-platform/opencti-graphql/src/generated/graphql.ts +++ b/opencti-platform/opencti-graphql/src/generated/graphql.ts @@ -11083,6 +11083,7 @@ export type IngestionCsv = BasicObject & InternalObject & { entity_type: Scalars['String']['output']; id: Scalars['ID']['output']; ingestion_running?: Maybe; + last_execution_date?: Maybe; markings?: Maybe>; name: Scalars['String']['output']; parent_types: Array>; @@ -11137,6 +11138,7 @@ export type IngestionRss = BasicObject & InternalObject & { entity_type: Scalars['String']['output']; id: Scalars['ID']['output']; ingestion_running?: Maybe; + last_execution_date?: Maybe; name: Scalars['String']['output']; parent_types: Array; report_types?: Maybe>; @@ -36410,6 +36412,7 @@ export type IngestionCsvResolvers; id?: Resolver; ingestion_running?: Resolver, ParentType, ContextType>; + last_execution_date?: Resolver, ParentType, ContextType>; markings?: Resolver>, ParentType, ContextType>; name?: Resolver; parent_types?: Resolver>, ParentType, ContextType>; @@ -36442,6 +36445,7 @@ export type IngestionRssResolvers; id?: Resolver; ingestion_running?: Resolver, ParentType, ContextType>; + last_execution_date?: Resolver, ParentType, ContextType>; name?: Resolver; parent_types?: Resolver, ParentType, ContextType>; report_types?: Resolver>, ParentType, ContextType>; diff --git a/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts b/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts index f97745d22c00..04657f5f11c3 100644 --- a/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts +++ b/opencti-platform/opencti-graphql/src/manager/ingestionManager.ts @@ -6,13 +6,14 @@ import { v4 as uuidv4 } from 'uuid'; import { clearIntervalAsync, setIntervalAsync } from 'set-interval-async/fixed'; import type { SetIntervalAsyncTimer } from 'set-interval-async/fixed'; import type { Moment } from 'moment'; +import { AxiosError } from 'axios'; import { lockResource } from '../database/redis'; import conf, { booleanConf, logApp } from '../config/conf'; import { TYPE_LOCK_ERROR, UnsupportedError } from '../config/errors'; import { executionContext, SYSTEM_USER } from '../utils/access'; import { type GetHttpClient, getHttpClient, OpenCTIHeaders } from '../utils/http-client'; import { isEmptyField, isNotEmptyField } from '../database/utils'; -import { FROM_START_STR, now, sanitizeForMomentParsing, utcDate } from '../utils/format'; +import { FROM_START_STR, now, sanitizeForMomentParsing, sinceNowInMinutes, utcDate } from '../utils/format'; import { generateStandardId } from '../schema/identifier'; import { ENTITY_TYPE_CONTAINER_REPORT } from '../schema/stixDomainObject'; import { pushToWorkerForConnector } from '../database/rabbitmq'; @@ -47,6 +48,9 @@ import type { CsvMapperParsed } from '../modules/internal/csvMapper/csvMapper-ty // If the lock is free, every API as the right to take it. const SCHEDULE_TIME = conf.get('ingestion_manager:interval') || 30000; const INGESTION_MANAGER_KEY = conf.get('ingestion_manager:lock_key') || 'ingestion_manager_lock'; +const RSS_FEED_MIN_INTERVAL_MINUTES = conf.get('ingestion_manager:rss_feed:min_interval_minutes') || 5; +const RSS_FEED_USER_AGENT = conf.get('ingestion_manager:rss_feed:user_agent') || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0'; +const CSV_FEED_MIN_INTERVAL_MINUTES = conf.get('ingestion_manager:csv_feed:min_interval_minutes') || 5; let running = false; @@ -61,6 +65,11 @@ const asArray = (data: unknown) => { return []; }; +const shouldExecuteIngestion = (ingestion: BasicStoreEntityIngestionRss | BasicStoreEntityIngestionCsv, min_interval_minutes: number) => { + const { last_execution_date } = ingestion; + return !last_execution_date || sinceNowInMinutes(last_execution_date) >= min_interval_minutes; +}; + interface UpdateInfo { state?: any buffering?: boolean @@ -176,7 +185,7 @@ const rssItemV2Convert = (turndownService: TurndownService, channel: RssElement, const rssHttpGetter = (): Getter => { const httpClientOptions: GetHttpClient = { responseType: 'text', - headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0' } + headers: { 'User-Agent': RSS_FEED_USER_AGENT } }; const httpClient = getHttpClient(httpClientOptions); return async (uri: string) => { @@ -242,11 +251,14 @@ const rssDataHandler = async (context: AuthContext, httpRssGet: Getter, turndown await pushBundleToConnectorQueue(context, ingestion, bundle); // Update the state lastPubDate = R.last(items)?.pubDate; - await patchRssIngestion(context, SYSTEM_USER, ingestion.internal_id, { current_state_date: lastPubDate }); + logApp.info('[OPENCTI-MODULE] lastPubDate:', { lastPubDate }); + await patchRssIngestion(context, SYSTEM_USER, ingestion.internal_id, { current_state_date: lastPubDate, last_execution_date: now() }); // Patch the related connector const state = { current_state_date: lastPubDate }; await updateBuiltInConnectorInfo(context, ingestion.user_id, ingestion.id, { state }); } else { + logApp.info('[OPENCTI-MODULE] Rss ingestion execution done, but no new item to ingest.'); + await patchRssIngestion(context, SYSTEM_USER, ingestion.internal_id, { last_execution_date: now() }); await updateBuiltInConnectorInfo(context, ingestion.user_id, ingestion.id); } }; @@ -263,16 +275,26 @@ const rssExecutor = async (context: AuthContext, turndownService: TurndownServic const ingestionPromises = []; for (let i = 0; i < ingestions.length; i += 1) { const ingestion = ingestions[i]; - // If ingestion have remaining messages in the queue, dont fetch any new data + // If ingestion have remaining messages in the queue, or if last execution was done before RSS_FEED_MIN_INTERVAL_MINUTES minutes, dont fetch any new data const { messages_number, messages_size } = await queueDetails(connectorIdFromIngestId(ingestion.id)); - if (messages_number === 0) { + if (messages_number === 0 && shouldExecuteIngestion(ingestion, RSS_FEED_MIN_INTERVAL_MINUTES)) { const ingestionPromise = rssDataHandler(context, httpGet, turndownService, ingestion) .catch((e) => { logApp.warn('[OPENCTI-MODULE] INGESTION - RSS ingestion execution', { cause: e, name: ingestion.name }); + if (e instanceof AxiosError) { + if (e?.response?.headers) { + if (e.response.headers['cf-mitigated']) { + logApp.warn(`[OPENCTI-MODULE] INGESTION Rss - Cloudflare challenge fail for ${ingestion.uri}`); + } + } + } + // In case of error we need also to take in account the min_interval_minutes with last_execution_date update. + patchRssIngestion(context, SYSTEM_USER, ingestion.internal_id, { last_execution_date: now() }); }); ingestionPromises.push(ingestionPromise); } else { // Update the state + logApp.info(`[OPENCTI-MODULE] INGESTION Rss, skipping ${ingestion.name} - queue already filled with messages (${messages_number}) or last run is more recent than ${RSS_FEED_MIN_INTERVAL_MINUTES} minutes.`); const ingestionPromise = updateBuiltInConnectorInfo(context, ingestion.user_id, ingestion.id, { buffering: true, messages_size }); ingestionPromises.push(ingestionPromise); } @@ -492,8 +514,8 @@ export const processCsvLines = async ( objectsInBundleCount = objectCount; await reportExpectation(context, ingestionUser, work.id);// csv file ends = 1 operation done. - logApp.info(`[OPENCTI-MODULE] INGESTION - Sent: ${bundleCount} bundles for ${objectsInBundleCount} objects.`); - const state = { current_state_hash: hashedIncomingData, added_after_start: utcDate(addedLast) }; + logApp.info(`[OPENCTI-MODULE] INGESTION Csv - Sent: ${bundleCount} bundles for ${objectsInBundleCount} objects.`); + const state = { current_state_hash: hashedIncomingData, added_after_start: utcDate(addedLast), last_execution_date: now() }; await patchCsvIngestion(context, SYSTEM_USER, ingestion.internal_id, state); await updateBuiltInConnectorInfo(context, ingestion.user_id, ingestion.id, { state }); } @@ -505,8 +527,14 @@ const csvDataHandler = async (context: AuthContext, ingestion: BasicStoreEntityI const csvMapper = await findById(context, user, ingestion.csv_mapper_id); const csvMapperParsed = parseCsvMapper(csvMapper); csvMapperParsed.user_chosen_markings = ingestion.markings ?? []; - const { csvLines, addedLast } = await fetchCsvFromUrl(csvMapperParsed, ingestion); - await processCsvLines(context, ingestion, csvMapperParsed, csvLines, addedLast); + try { + const { csvLines, addedLast } = await fetchCsvFromUrl(csvMapperParsed, ingestion); + await processCsvLines(context, ingestion, csvMapperParsed, csvLines, addedLast); + } catch (e: any) { + logApp.error(`[OPENCTI-MODULE] INGESTION Csv - Error trying to fetch csv feed for: ${ingestion.name}`); + logApp.error(e, { ingestion }); + throw e; + } }; const csvExecutor = async (context: AuthContext) => { @@ -520,16 +548,18 @@ const csvExecutor = async (context: AuthContext) => { const ingestionPromises = []; for (let i = 0; i < ingestions.length; i += 1) { const ingestion = ingestions[i]; - // If ingestion have remaining messages in the queue, dont fetch any new data const { messages_number, messages_size } = await queueDetails(connectorIdFromIngestId(ingestion.id)); - if (messages_number === 0) { + if (messages_number === 0 && shouldExecuteIngestion(ingestion, CSV_FEED_MIN_INTERVAL_MINUTES)) { const ingestionPromise = csvDataHandler(context, ingestion) .catch((e) => { logApp.warn('[OPENCTI-MODULE] INGESTION - CSV ingestion execution', { cause: e, name: ingestion.name }); + // In case of error we need also to take in account the min_interval_minutes with last_execution_date update. + patchCsvIngestion(context, SYSTEM_USER, ingestion.internal_id, { last_execution_date: now() }); }); ingestionPromises.push(ingestionPromise); } else { // Update the state + logApp.info(`[OPENCTI-MODULE] INGESTION csv, skipping ${ingestion.name} - queue already filled with messages (${messages_number}) or last run is more recent than ${RSS_FEED_MIN_INTERVAL_MINUTES} minutes.`); const ingestionPromise = updateBuiltInConnectorInfo(context, ingestion.user_id, ingestion.id, { buffering: true, messages_size }); ingestionPromises.push(ingestionPromise); } diff --git a/opencti-platform/opencti-graphql/src/migrations/1733912620469-clean-deprecated-rels.js b/opencti-platform/opencti-graphql/src/migrations/1733912620469-clean-deprecated-rels.js new file mode 100644 index 000000000000..591005bcc99b --- /dev/null +++ b/opencti-platform/opencti-graphql/src/migrations/1733912620469-clean-deprecated-rels.js @@ -0,0 +1,168 @@ +import * as R from 'ramda'; +import { Promise } from 'bluebird'; +import { logApp } from '../config/conf'; +import { BULK_TIMEOUT, elBulk, elFindByIds, elList, elUpdateByQueryForMigration, ES_MAX_CONCURRENCY, MAX_BULK_OPERATIONS } from '../database/engine'; +import { READ_INDEX_STIX_DOMAIN_OBJECTS } from '../database/utils'; +import { executionContext, SYSTEM_USER } from '../utils/access'; +import { + ENTITY_TYPE_CAMPAIGN, + ENTITY_TYPE_IDENTITY_SECTOR, + ENTITY_TYPE_INCIDENT, + ENTITY_TYPE_INTRUSION_SET, + ENTITY_TYPE_LOCATION_CITY, + ENTITY_TYPE_LOCATION_COUNTRY, + ENTITY_TYPE_LOCATION_REGION, + ENTITY_TYPE_MALWARE, + ENTITY_TYPE_THREAT_ACTOR_GROUP +} from '../schema/stixDomainObject'; +import { ENTITY_TYPE_THREAT_ACTOR_INDIVIDUAL } from '../modules/threatActorIndividual/threatActorIndividual-types'; +import { ENTITY_IPV4_ADDR, ENTITY_IPV6_ADDR, isStixCyberObservable } from '../schema/stixCyberObservable'; + +const message = '[MIGRATION] Cleaning deprecated rels'; + +export const up = async (next) => { + logApp.info(`${message} > started`); + const context = executionContext('migration'); + + // 1st pass + // Cleaning all threats from any re_related-to related to observables + const relKeyRelatedTo = 'rel_related-to.internal_id'; + const bulkOperationsRelatedTo = []; + const startRelatedTo = new Date().getTime(); + const clearRelatedToObservableRels = async (threats) => { + for (let i = 0; i < threats.length; i += 1) { + const threat = threats[i]; + const relatedToIds = threat[relKeyRelatedTo] ?? []; + const newIds = []; + const groupIds = R.splitEvery(5000, relatedToIds); + for (let index = 0; index < groupIds.length; index += 1) { + const workingIds = groupIds[index]; + const entitiesBaseData = await elFindByIds(context, SYSTEM_USER, workingIds, { baseData: true }); + const entitiesIdsWithoutObservables = entitiesBaseData.filter((n) => !isStixCyberObservable(n.entity_type)).map((n) => n.internal_id); + newIds.push(...entitiesIdsWithoutObservables); + } + if (newIds.length > 0) { + const updateQuery = [ + { update: { _index: threat._index, _id: threat.internal_id, retry_on_conflict: 5 } }, + { doc: { [relKeyRelatedTo]: newIds } }, + ]; + bulkOperationsRelatedTo.push(...updateQuery); + } else { + const updateQuery = [ + { update: { _index: threat._index, _id: threat.internal_id, retry_on_conflict: 5 } }, + { script: `ctx._source.remove('${relKeyRelatedTo}')` } + ]; + bulkOperationsRelatedTo.push(...updateQuery); + } + } + }; + const threatTypes = [ + ENTITY_TYPE_THREAT_ACTOR_GROUP, + ENTITY_TYPE_THREAT_ACTOR_INDIVIDUAL, + ENTITY_TYPE_INTRUSION_SET, + ENTITY_TYPE_CAMPAIGN, + ENTITY_TYPE_MALWARE, + ENTITY_TYPE_INCIDENT, + ]; + const optsRelatedTo = { types: threatTypes, callback: clearRelatedToObservableRels }; + await elList(context, SYSTEM_USER, READ_INDEX_STIX_DOMAIN_OBJECTS, optsRelatedTo); + // Apply operations. + let currentProcessingRelatedTo = 0; + const groupsOfOperationsRelatedTo = R.splitEvery(MAX_BULK_OPERATIONS, bulkOperationsRelatedTo); + const concurrentUpdateRelatedTo = async (bulk) => { + await elBulk({ refresh: true, timeout: BULK_TIMEOUT, body: bulk }); + currentProcessingRelatedTo += bulk.length; + logApp.info(`[OPENCTI] Cleaning deprecated rels for observable related-to ${currentProcessingRelatedTo} / ${bulkOperationsRelatedTo.length}`); + }; + await Promise.map(groupsOfOperationsRelatedTo, concurrentUpdateRelatedTo, { concurrency: ES_MAX_CONCURRENCY }); + logApp.info(`[MIGRATION] Cleaning deprecated rels for observable related-to done in ${new Date() - startRelatedTo} ms`); + + // 2nd pass + // Cleaning located-at when pointing a country or a region + const relKeyLocatedAt = 'rel_located-at.internal_id'; + const bulkOperationsLocatedAt = []; + const startLocatedAt = new Date().getTime(); + const cleanTypes = [ENTITY_IPV4_ADDR, ENTITY_IPV6_ADDR, ENTITY_TYPE_LOCATION_CITY]; + const clearLocatedAtRegionAndCountryRels = async (locations) => { + for (let i = 0; i < locations.length; i += 1) { + const location = locations[i]; + const locatedAtIds = location[relKeyLocatedAt] ?? []; + const newIds = []; + const groupIds = R.splitEvery(5000, locatedAtIds); + for (let index = 0; index < groupIds.length; index += 1) { + const workingIds = groupIds[index]; + const entitiesBaseData = await elFindByIds(context, SYSTEM_USER, workingIds, { baseData: true }); + const entitiesIdsOnlyWithoutClearedTypes = entitiesBaseData.filter((n) => !cleanTypes.includes(n.entity_type)).map((n) => n.internal_id); + newIds.push(...entitiesIdsOnlyWithoutClearedTypes); + } + if (newIds.length > 0) { + const updateQuery = [ + { update: { _index: location._index, _id: location.internal_id, retry_on_conflict: 5 } }, + { doc: { [relKeyLocatedAt]: newIds } }, + ]; + bulkOperationsLocatedAt.push(...updateQuery); + } else { + const updateQuery = [ + { update: { _index: location._index, _id: location.internal_id, retry_on_conflict: 5 } }, + { script: `ctx._source.remove('${relKeyLocatedAt}')` } + ]; + bulkOperationsLocatedAt.push(...updateQuery); + } + } + }; + const opts = { types: [ENTITY_TYPE_LOCATION_REGION, ENTITY_TYPE_LOCATION_COUNTRY], callback: clearLocatedAtRegionAndCountryRels }; + await elList(context, SYSTEM_USER, READ_INDEX_STIX_DOMAIN_OBJECTS, opts); + // Apply operations. + let currentProcessing = 0; + const groupsOfOperations = R.splitEvery(MAX_BULK_OPERATIONS, bulkOperationsLocatedAt); + const concurrentUpdate = async (bulk) => { + await elBulk({ refresh: true, timeout: BULK_TIMEOUT, body: bulk }); + currentProcessing += bulk.length; + logApp.info(`[OPENCTI] Cleaning deprecated rels for located-at to country / region ${currentProcessing} / ${bulkOperationsLocatedAt.length}`); + }; + await Promise.map(groupsOfOperations, concurrentUpdate, { concurrency: ES_MAX_CONCURRENCY }); + logApp.info(`[MIGRATION] Cleaning deprecated rels for located-at to country / region in ${new Date() - startLocatedAt} ms`); + + // 3rd pass + // Cleaning all targets to countries, regions and sectors + const updateQueryForTargets = { + script: { + params: { fieldToRemove: 'rel_targets' }, + source: 'ctx._source.remove(params.fieldToRemove)', + }, + query: { + bool: { + should: [ + { + bool: { + must: [{ term: { 'entity_type.keyword': { value: ENTITY_TYPE_LOCATION_REGION } } }], + } + }, + { + bool: { + must: [{ term: { 'entity_type.keyword': { value: ENTITY_TYPE_LOCATION_COUNTRY } } }], + } + }, + { + bool: { + must: [{ term: { 'entity_type.keyword': { value: ENTITY_TYPE_IDENTITY_SECTOR } } }], + } + }, + ], + minimum_should_match: 1, + }, + }, + }; + await elUpdateByQueryForMigration( + message, + READ_INDEX_STIX_DOMAIN_OBJECTS, + updateQueryForTargets + ); + + logApp.info(`${message} > done`); + next(); +}; + +export const down = async (next) => { + next(); +}; diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql index 43a5c91a2fb3..f56558333d81 100644 --- a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.graphql @@ -17,6 +17,7 @@ type IngestionCsv implements InternalObject & BasicObject { ingestion_running: Boolean current_state_hash: String current_state_date: DateTime + last_execution_date: DateTime markings: [String!] } diff --git a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts index 4c86bcdd376b..8601aea1b96f 100644 --- a/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts +++ b/opencti-platform/opencti-graphql/src/modules/ingestion/ingestion-csv.ts @@ -35,6 +35,7 @@ const INGESTION_CSV_DEFINITION: ModuleDefinition { expect(queryResult).not.toBeNull(); expect(queryResult.data.city).not.toBeNull(); expect(queryResult.data.city.standard_id).toEqual('location--ce920c5b-03ea-576d-ac1d-701d9d7a1bed'); - expect(queryResult.data.city.country.standard_id).toEqual('location--b8d0549f-de06-5ebd-a6e9-d31a581dba5d'); }); it('should list cities', async () => { const queryResult = await queryAsAdmin({ query: LIST_QUERY, variables: { first: 10 } }); diff --git a/opencti-platform/opencti-graphql/yarn.lock b/opencti-platform/opencti-graphql/yarn.lock index d310aac558db..976c265b0d35 100644 --- a/opencti-platform/opencti-graphql/yarn.lock +++ b/opencti-platform/opencti-graphql/yarn.lock @@ -7792,9 +7792,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:7.37.3": - version: 7.37.3 - resolution: "eslint-plugin-react@npm:7.37.3" +"eslint-plugin-react@npm:7.37.4": + version: 7.37.4 + resolution: "eslint-plugin-react@npm:7.37.4" dependencies: array-includes: "npm:^3.1.8" array.prototype.findlast: "npm:^1.2.5" @@ -7816,7 +7816,7 @@ __metadata: string.prototype.repeat: "npm:^1.0.0" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - checksum: 10/30042b95c440a962157192f67369d8c9171f7c73e992695e5674c9d28c3cfe4098488eca86dfa7c433d4c8a91243cbafdc80c7e42d2c1720a427ddb36e65457e + checksum: 10/c538c10665c87cb90a0bcc4efe53a758570db10997d079d31474a9760116ef5584648fa22403d889ca672df8071bda10b40434ea0499e5ee8360bc5c8aba1679 languageName: node linkType: hard @@ -8050,16 +8050,16 @@ __metadata: languageName: node linkType: hard -"express@npm:4.21.0": - version: 4.21.0 - resolution: "express@npm:4.21.0" +"express@npm:4.21.2": + version: 4.21.2 + resolution: "express@npm:4.21.2" dependencies: accepts: "npm:~1.3.8" array-flatten: "npm:1.1.1" body-parser: "npm:1.20.3" content-disposition: "npm:0.5.4" content-type: "npm:~1.0.4" - cookie: "npm:0.6.0" + cookie: "npm:0.7.1" cookie-signature: "npm:1.0.6" debug: "npm:2.6.9" depd: "npm:2.0.0" @@ -8073,7 +8073,7 @@ __metadata: methods: "npm:~1.1.2" on-finished: "npm:2.4.1" parseurl: "npm:~1.3.3" - path-to-regexp: "npm:0.1.10" + path-to-regexp: "npm:0.1.12" proxy-addr: "npm:~2.0.7" qs: "npm:6.13.0" range-parser: "npm:~1.2.1" @@ -8085,7 +8085,7 @@ __metadata: type-is: "npm:~1.6.18" utils-merge: "npm:1.0.1" vary: "npm:~1.1.2" - checksum: 10/3b1ee5bc5b1bd996f688702519cebc9b63a24e506965f6e1773268238cfa2c24ffdb38cc3fcb4fde66f77de1c0bebd9ee058dad06bb9c6f084b525f3c09164d3 + checksum: 10/34571c442fc8c9f2c4b442d2faa10ea1175cf8559237fc6a278f5ce6254a8ffdbeb9a15d99f77c1a9f2926ab183e3b7ba560e3261f1ad4149799e3412ab66bd1 languageName: node linkType: hard @@ -9014,12 +9014,12 @@ __metadata: languageName: node linkType: hard -"graphql-ws@npm:5.16.1": - version: 5.16.1 - resolution: "graphql-ws@npm:5.16.1" +"graphql-ws@npm:5.16.2": + version: 5.16.2 + resolution: "graphql-ws@npm:5.16.2" peerDependencies: graphql: ">=0.11 <=16" - checksum: 10/ce82ddcb1eb705ea87dac3b454562de9abaec04beef0a3726ffaaa5e6c3cd478217c82d7a9d07902c936c5567db50a0a4d3c95c4274d1b088aeaacb03833f0a7 + checksum: 10/6647bfe640b467f27aaf5ee044c1d114fe266e82cda4ebbb4368d5a4e98df5d2de9d6be70d28eb5e821d87fbf8964c3a8a18abf87c76d4f148800fd8e0488c3d languageName: node linkType: hard @@ -10491,10 +10491,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:11.0.0": - version: 11.0.0 - resolution: "lru-cache@npm:11.0.0" - checksum: 10/41f36fbff8b6f199cce3e9cb2b625714f97a535dfd7f16d0988c2627f9ed4c38b6dc8f9ea7fdba19262a7c917ba41c89cad15ca3e3791fc9a2068af472b5bc8d +"lru-cache@npm:11.0.2": + version: 11.0.2 + resolution: "lru-cache@npm:11.0.2" + checksum: 10/25fcb66e9d91eaf17227c6abfe526a7bed5903de74f93bfde380eb8a13410c5e8d3f14fe447293f3f322a7493adf6f9f015c6f1df7a235ff24ec30f366e1c058 languageName: node linkType: hard @@ -11087,10 +11087,10 @@ __metadata: languageName: node linkType: hard -"nodemailer@npm:6.9.15": - version: 6.9.15 - resolution: "nodemailer@npm:6.9.15" - checksum: 10/16079406fe603f6e4debb8ac34bdb919688ad169ddf61e7b10e56abef85b537762eb3a19a15ce32bb9446d68e2f93a1ba4e24836ba86c7363fc42145aa36a291 +"nodemailer@npm:6.9.16": + version: 6.9.16 + resolution: "nodemailer@npm:6.9.16" + checksum: 10/f131888d3111238fde4ee03539e62f1764b99365ff31d556dde0367dfefcee1f2eb8948558f35ba84fe5cd805f2d01294eee63a5675d3aa501e7df548a2518ce languageName: node linkType: hard @@ -11459,10 +11459,10 @@ __metadata: eslint-plugin-import: "npm:2.31.0" eslint-plugin-import-newlines: "npm:1.4.0" eslint-plugin-jsx-a11y: "npm:6.10.2" - eslint-plugin-react: "npm:7.37.3" + eslint-plugin-react: "npm:7.37.4" eslint-plugin-react-hooks: "npm:4.6.2" eventsource: "npm:2.0.2" - express: "npm:4.21.0" + express: "npm:4.21.2" express-rate-limit: "npm:7.5.0" express-session: "npm:1.18.1" fast-glob: "npm:3.3.3" @@ -11479,7 +11479,7 @@ __metadata: graphql-subscriptions: "npm:2.0.0" graphql-tag: "npm:2.12.6" graphql-upload: "npm:17.0.0" - graphql-ws: "npm:5.16.1" + graphql-ws: "npm:5.16.2" helmet: "npm:7.2.0" http-proxy-agent: "npm:7.0.2" https-proxy-agent: "npm:7.0.6" @@ -11489,7 +11489,7 @@ __metadata: js-deep-equals: "npm:2.1.1" json-to-plain-text: "npm:1.1.4" jwt-decode: "npm:4.0.0" - lru-cache: "npm:11.0.0" + lru-cache: "npm:11.0.2" migrate: "npm:2.1.0" mime-types: "npm:2.1.35" moment: "npm:2.30.1" @@ -11498,7 +11498,7 @@ __metadata: node-calls-python: "npm:1.9.1" node-fetch: "npm:3.3.2" node-forge: "npm:1.3.1" - nodemailer: "npm:6.9.15" + nodemailer: "npm:6.9.16" openai: "npm:4.78.1" openid-client: "npm:5.6.5" opentelemetry-node-metrics: "npm:3.0.0" @@ -11519,9 +11519,9 @@ __metadata: showdown: "npm:2.1.0" source-map-support: "npm:0.5.21" tough-cookie: "npm:5.1.0" - ts-loader: "npm:9.5.1" + ts-loader: "npm:9.5.2" turndown: "npm:7.2.0" - typescript: "npm:5.7.2" + typescript: "npm:5.7.3" unzipper: "npm:0.12.3" uuid: "npm:11.0.3" uuid-time: "npm:1.0.0" @@ -13669,9 +13669,9 @@ __metadata: languageName: node linkType: hard -"ts-loader@npm:9.5.1": - version: 9.5.1 - resolution: "ts-loader@npm:9.5.1" +"ts-loader@npm:9.5.2": + version: 9.5.2 + resolution: "ts-loader@npm:9.5.2" dependencies: chalk: "npm:^4.1.0" enhanced-resolve: "npm:^5.0.0" @@ -13681,7 +13681,7 @@ __metadata: peerDependencies: typescript: "*" webpack: ^5.0.0 - checksum: 10/a85d43bb6f72858d613290ac02d1d24e81c38ba2dcb98b90465dc97eb6c2036bf9a389542c1a7865548643e7ed39f063fdff2dbb3e5aafbc511de6a3eb275adf + checksum: 10/b2d0a4ae9eab459586580e6f83a4351fa0568ccd4d9b41b42368390c95335f98562120cd63c84b6008548ee7af13520a8b79c14b2e8114058104cf7cfb39873d languageName: node linkType: hard @@ -13888,23 +13888,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.7.2": - version: 5.7.2 - resolution: "typescript@npm:5.7.2" +"typescript@npm:5.7.3": + version: 5.7.3 + resolution: "typescript@npm:5.7.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/4caa3904df69db9d4a8bedc31bafc1e19ffb7b24fbde2997a1633ae1398d0de5bdbf8daf602ccf3b23faddf1aeeb9b795223a2ed9c9a4fdcaf07bfde114a401a + checksum: 10/6a7e556de91db3d34dc51cd2600e8e91f4c312acd8e52792f243c7818dfadb27bae677175fad6947f9c81efb6c57eb6b2d0c736f196a6ee2f1f7d57b74fc92fa languageName: node linkType: hard -"typescript@patch:typescript@npm%3A5.7.2#optional!builtin": - version: 5.7.2 - resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin::version=5.7.2&hash=5786d5" +"typescript@patch:typescript@npm%3A5.7.3#optional!builtin": + version: 5.7.3 + resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin::version=5.7.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/d75ca10141afc64fd3474b41a8b082b640555bed388d237558aed64e5827ddadb48f90932c7f4205883f18f5bcab8b6a739a2cfac95855604b0dfeb34bc2f3eb + checksum: 10/dc58d777eb4c01973f7fbf1fd808aad49a0efdf545528dab9b07d94fdcb65b8751742804c3057e9619a4627f2d9cc85547fdd49d9f4326992ad0181b49e61d81 languageName: node linkType: hard @@ -14000,9 +14000,9 @@ __metadata: linkType: hard "undici@npm:^6.12.0": - version: 6.20.1 - resolution: "undici@npm:6.20.1" - checksum: 10/68604b53754a95ec89d52efc08fe3e70e333997300c9a5b69f2b6496f1f0f568b2e35adec6442985a7b1d2f7a5648ef5062d1736e4d68082d473cb82177674bc + version: 6.21.1 + resolution: "undici@npm:6.21.1" + checksum: 10/eeccc07e9073ae8e755fdc0dc8cdfaa426c01ec6f815425c3ecedba2e5394cea4993962c040dd168951714a82f0d001a13018c3ae3ad4534f0fa97afe425c08d languageName: node linkType: hard @@ -14239,8 +14239,8 @@ __metadata: linkType: hard "vite@npm:^5.0.0": - version: 5.4.10 - resolution: "vite@npm:5.4.10" + version: 5.4.14 + resolution: "vite@npm:5.4.14" dependencies: esbuild: "npm:^0.21.3" fsevents: "npm:~2.3.3" @@ -14277,7 +14277,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/5d4a427d585d6f9114fc383114f707dca46408f54b221709e5eb6b0c16e0b4dec4baf908a7db9a8f1e5b16e64b655900ac14629abe61c698cbe296115c65ed8a + checksum: 10/ce382f4059eb6c939823b8f62163794752243755d84c71a4b73ad0f7d4d9f4c7a557a6ef4c78e0640f4bcf5ae5ec6b20c7ee4816419af3c81ba275f478b73468 languageName: node linkType: hard