diff --git a/6.2.X/404.html b/6.2.X/404.html new file mode 100755 index 00000000..b706c06e --- /dev/null +++ b/6.2.X/404.html @@ -0,0 +1,4642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + +
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/assets/attribution_propagation.png b/6.2.X/administration/assets/attribution_propagation.png new file mode 100755 index 00000000..f4492112 Binary files /dev/null and b/6.2.X/administration/assets/attribution_propagation.png differ diff --git a/6.2.X/administration/assets/audit.png b/6.2.X/administration/assets/audit.png new file mode 100755 index 00000000..58ac12c2 Binary files /dev/null and b/6.2.X/administration/assets/audit.png differ diff --git a/6.2.X/administration/assets/audit_configuration.png b/6.2.X/administration/assets/audit_configuration.png new file mode 100755 index 00000000..3314da03 Binary files /dev/null and b/6.2.X/administration/assets/audit_configuration.png differ diff --git a/6.2.X/administration/assets/authentication-strategies.png b/6.2.X/administration/assets/authentication-strategies.png new file mode 100755 index 00000000..9cf0f75d Binary files /dev/null and b/6.2.X/administration/assets/authentication-strategies.png differ diff --git a/6.2.X/administration/assets/authorize-access.gif b/6.2.X/administration/assets/authorize-access.gif new file mode 100755 index 00000000..952155c1 Binary files /dev/null and b/6.2.X/administration/assets/authorize-access.gif differ diff --git a/6.2.X/administration/assets/belonging_propagation.png b/6.2.X/administration/assets/belonging_propagation.png new file mode 100755 index 00000000..41d1da76 Binary files /dev/null and b/6.2.X/administration/assets/belonging_propagation.png differ diff --git a/6.2.X/administration/assets/broadcast_message_configuration.png b/6.2.X/administration/assets/broadcast_message_configuration.png new file mode 100755 index 00000000..a399e2f5 Binary files /dev/null and b/6.2.X/administration/assets/broadcast_message_configuration.png differ diff --git a/6.2.X/administration/assets/broadcast_message_display_dismissible.png b/6.2.X/administration/assets/broadcast_message_display_dismissible.png new file mode 100755 index 00000000..d32572e4 Binary files /dev/null and b/6.2.X/administration/assets/broadcast_message_display_dismissible.png differ diff --git a/6.2.X/administration/assets/broadcast_message_display_notdismissible.png b/6.2.X/administration/assets/broadcast_message_display_notdismissible.png new file mode 100755 index 00000000..dcdfe558 Binary files /dev/null and b/6.2.X/administration/assets/broadcast_message_display_notdismissible.png differ diff --git a/6.2.X/administration/assets/case_templates.png b/6.2.X/administration/assets/case_templates.png new file mode 100755 index 00000000..5572f627 Binary files /dev/null and b/6.2.X/administration/assets/case_templates.png differ diff --git a/6.2.X/administration/assets/confidence_scale_configuration.png b/6.2.X/administration/assets/confidence_scale_configuration.png new file mode 100755 index 00000000..2d9bf274 Binary files /dev/null and b/6.2.X/administration/assets/confidence_scale_configuration.png differ diff --git a/6.2.X/administration/assets/create-entity-marking-filter.png b/6.2.X/administration/assets/create-entity-marking-filter.png new file mode 100755 index 00000000..b13902dc Binary files /dev/null and b/6.2.X/administration/assets/create-entity-marking-filter.png differ diff --git a/6.2.X/administration/assets/create-entity-markings.png b/6.2.X/administration/assets/create-entity-markings.png new file mode 100755 index 00000000..d222c0b7 Binary files /dev/null and b/6.2.X/administration/assets/create-entity-markings.png differ diff --git a/6.2.X/administration/assets/create-marking.gif b/6.2.X/administration/assets/create-marking.gif new file mode 100755 index 00000000..e995d877 Binary files /dev/null and b/6.2.X/administration/assets/create-marking.gif differ diff --git a/6.2.X/administration/assets/create-role.png b/6.2.X/administration/assets/create-role.png new file mode 100755 index 00000000..62cc069e Binary files /dev/null and b/6.2.X/administration/assets/create-role.png differ diff --git a/6.2.X/administration/assets/create-user-new.png b/6.2.X/administration/assets/create-user-new.png new file mode 100755 index 00000000..a44d574d Binary files /dev/null and b/6.2.X/administration/assets/create-user-new.png differ diff --git a/6.2.X/administration/assets/csv-mapper-attribute.png b/6.2.X/administration/assets/csv-mapper-attribute.png new file mode 100755 index 00000000..3e9ca584 Binary files /dev/null and b/6.2.X/administration/assets/csv-mapper-attribute.png differ diff --git a/6.2.X/administration/assets/csv-mapper-default.png b/6.2.X/administration/assets/csv-mapper-default.png new file mode 100755 index 00000000..45067a80 Binary files /dev/null and b/6.2.X/administration/assets/csv-mapper-default.png differ diff --git a/6.2.X/administration/assets/csv-mapper-settings.png b/6.2.X/administration/assets/csv-mapper-settings.png new file mode 100755 index 00000000..19d4749f Binary files /dev/null and b/6.2.X/administration/assets/csv-mapper-settings.png differ diff --git a/6.2.X/administration/assets/csv-mappers-create.png b/6.2.X/administration/assets/csv-mappers-create.png new file mode 100755 index 00000000..d33a0b98 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-create.png differ diff --git a/6.2.X/administration/assets/csv-mappers-default-markings.png b/6.2.X/administration/assets/csv-mappers-default-markings.png new file mode 100755 index 00000000..0f342135 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-default-markings.png differ diff --git a/6.2.X/administration/assets/csv-mappers-default-values.png b/6.2.X/administration/assets/csv-mappers-default-values.png new file mode 100755 index 00000000..f0aaf458 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-default-values.png differ diff --git a/6.2.X/administration/assets/csv-mappers-field-options-1.png b/6.2.X/administration/assets/csv-mappers-field-options-1.png new file mode 100755 index 00000000..4d55afb1 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-field-options-1.png differ diff --git a/6.2.X/administration/assets/csv-mappers-invalid.png b/6.2.X/administration/assets/csv-mappers-invalid.png new file mode 100755 index 00000000..660a5af6 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-invalid.png differ diff --git a/6.2.X/administration/assets/csv-mappers-rep-1.png b/6.2.X/administration/assets/csv-mappers-rep-1.png new file mode 100755 index 00000000..dcc4765b Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-rep-1.png differ diff --git a/6.2.X/administration/assets/csv-mappers-test.png b/6.2.X/administration/assets/csv-mappers-test.png new file mode 100755 index 00000000..942798c9 Binary files /dev/null and b/6.2.X/administration/assets/csv-mappers-test.png differ diff --git a/6.2.X/administration/assets/custom-email-notifier.png b/6.2.X/administration/assets/custom-email-notifier.png new file mode 100755 index 00000000..9ef061c0 Binary files /dev/null and b/6.2.X/administration/assets/custom-email-notifier.png differ diff --git a/6.2.X/administration/assets/customization-window.png b/6.2.X/administration/assets/customization-window.png new file mode 100755 index 00000000..8f21a640 Binary files /dev/null and b/6.2.X/administration/assets/customization-window.png differ diff --git a/6.2.X/administration/assets/data-access-diagram.png b/6.2.X/administration/assets/data-access-diagram.png new file mode 100755 index 00000000..629af7c4 Binary files /dev/null and b/6.2.X/administration/assets/data-access-diagram.png differ diff --git a/6.2.X/administration/assets/decay-rule-created.png b/6.2.X/administration/assets/decay-rule-created.png new file mode 100755 index 00000000..5ccdbb27 Binary files /dev/null and b/6.2.X/administration/assets/decay-rule-created.png differ diff --git a/6.2.X/administration/assets/decay-rule-creation-filled.png b/6.2.X/administration/assets/decay-rule-creation-filled.png new file mode 100755 index 00000000..e92010bc Binary files /dev/null and b/6.2.X/administration/assets/decay-rule-creation-filled.png differ diff --git a/6.2.X/administration/assets/decay-rule-creation.png b/6.2.X/administration/assets/decay-rule-creation.png new file mode 100755 index 00000000..a7eda456 Binary files /dev/null and b/6.2.X/administration/assets/decay-rule-creation.png differ diff --git a/6.2.X/administration/assets/decay-rules.png b/6.2.X/administration/assets/decay-rules.png new file mode 100755 index 00000000..5d9edf58 Binary files /dev/null and b/6.2.X/administration/assets/decay-rules.png differ diff --git a/6.2.X/administration/assets/default-marking.gif b/6.2.X/administration/assets/default-marking.gif new file mode 100755 index 00000000..59c8aeeb Binary files /dev/null and b/6.2.X/administration/assets/default-marking.gif differ diff --git a/6.2.X/administration/assets/define_organization_admin.png b/6.2.X/administration/assets/define_organization_admin.png new file mode 100755 index 00000000..e9644386 Binary files /dev/null and b/6.2.X/administration/assets/define_organization_admin.png differ diff --git a/6.2.X/administration/assets/enterprise-activate.png b/6.2.X/administration/assets/enterprise-activate.png new file mode 100755 index 00000000..3c827cac Binary files /dev/null and b/6.2.X/administration/assets/enterprise-activate.png differ diff --git a/6.2.X/administration/assets/enterprise-eula.png b/6.2.X/administration/assets/enterprise-eula.png new file mode 100755 index 00000000..9b0c6ec4 Binary files /dev/null and b/6.2.X/administration/assets/enterprise-eula.png differ diff --git a/6.2.X/administration/assets/entity-configuration.png b/6.2.X/administration/assets/entity-configuration.png new file mode 100755 index 00000000..225007ca Binary files /dev/null and b/6.2.X/administration/assets/entity-configuration.png differ diff --git a/6.2.X/administration/assets/file-indexing-monitoring.png b/6.2.X/administration/assets/file-indexing-monitoring.png new file mode 100755 index 00000000..77d1358f Binary files /dev/null and b/6.2.X/administration/assets/file-indexing-monitoring.png differ diff --git a/6.2.X/administration/assets/file-indexing-settings.png b/6.2.X/administration/assets/file-indexing-settings.png new file mode 100755 index 00000000..a9e3a47e Binary files /dev/null and b/6.2.X/administration/assets/file-indexing-settings.png differ diff --git a/6.2.X/administration/assets/group-overview-new.png b/6.2.X/administration/assets/group-overview-new.png new file mode 100755 index 00000000..17592c21 Binary files /dev/null and b/6.2.X/administration/assets/group-overview-new.png differ diff --git a/6.2.X/administration/assets/hidden_entities_platform.png b/6.2.X/administration/assets/hidden_entities_platform.png new file mode 100755 index 00000000..b26510be Binary files /dev/null and b/6.2.X/administration/assets/hidden_entities_platform.png differ diff --git a/6.2.X/administration/assets/hidden_entities_role.png b/6.2.X/administration/assets/hidden_entities_role.png new file mode 100755 index 00000000..dbf308a4 Binary files /dev/null and b/6.2.X/administration/assets/hidden_entities_role.png differ diff --git a/6.2.X/administration/assets/identities_propagation.png b/6.2.X/administration/assets/identities_propagation.png new file mode 100755 index 00000000..0c677823 Binary files /dev/null and b/6.2.X/administration/assets/identities_propagation.png differ diff --git a/6.2.X/administration/assets/incident_on_sighting.png b/6.2.X/administration/assets/incident_on_sighting.png new file mode 100755 index 00000000..9811dbb9 Binary files /dev/null and b/6.2.X/administration/assets/incident_on_sighting.png differ diff --git a/6.2.X/administration/assets/items-to-be-deleted.png b/6.2.X/administration/assets/items-to-be-deleted.png new file mode 100755 index 00000000..4b32901b Binary files /dev/null and b/6.2.X/administration/assets/items-to-be-deleted.png differ diff --git a/6.2.X/administration/assets/local-password-policies.png b/6.2.X/administration/assets/local-password-policies.png new file mode 100755 index 00000000..96171f93 Binary files /dev/null and b/6.2.X/administration/assets/local-password-policies.png differ diff --git a/6.2.X/administration/assets/location_propagation.png b/6.2.X/administration/assets/location_propagation.png new file mode 100755 index 00000000..89b7b34f Binary files /dev/null and b/6.2.X/administration/assets/location_propagation.png differ diff --git a/6.2.X/administration/assets/locations_propagation_report.png b/6.2.X/administration/assets/locations_propagation_report.png new file mode 100755 index 00000000..72e61062 Binary files /dev/null and b/6.2.X/administration/assets/locations_propagation_report.png differ diff --git a/6.2.X/administration/assets/login-message-configuration.png b/6.2.X/administration/assets/login-message-configuration.png new file mode 100755 index 00000000..b3887f60 Binary files /dev/null and b/6.2.X/administration/assets/login-message-configuration.png differ diff --git a/6.2.X/administration/assets/login-message-illustration.png b/6.2.X/administration/assets/login-message-illustration.png new file mode 100755 index 00000000..05f16fee Binary files /dev/null and b/6.2.X/administration/assets/login-message-illustration.png differ diff --git a/6.2.X/administration/assets/main_entity_selection.png b/6.2.X/administration/assets/main_entity_selection.png new file mode 100755 index 00000000..75d78723 Binary files /dev/null and b/6.2.X/administration/assets/main_entity_selection.png differ diff --git a/6.2.X/administration/assets/mandatory_and_default_attributes.png b/6.2.X/administration/assets/mandatory_and_default_attributes.png new file mode 100755 index 00000000..89a2f9ea Binary files /dev/null and b/6.2.X/administration/assets/mandatory_and_default_attributes.png differ diff --git a/6.2.X/administration/assets/marking-pop.gif b/6.2.X/administration/assets/marking-pop.gif new file mode 100755 index 00000000..1972fff6 Binary files /dev/null and b/6.2.X/administration/assets/marking-pop.gif differ diff --git a/6.2.X/administration/assets/max-marking-shareable.png b/6.2.X/administration/assets/max-marking-shareable.png new file mode 100755 index 00000000..fa932358 Binary files /dev/null and b/6.2.X/administration/assets/max-marking-shareable.png differ diff --git a/6.2.X/administration/assets/merge_panel.png b/6.2.X/administration/assets/merge_panel.png new file mode 100755 index 00000000..be892537 Binary files /dev/null and b/6.2.X/administration/assets/merge_panel.png differ diff --git a/6.2.X/administration/assets/mitre_attack_matrix.png b/6.2.X/administration/assets/mitre_attack_matrix.png new file mode 100755 index 00000000..a10cf7a7 Binary files /dev/null and b/6.2.X/administration/assets/mitre_attack_matrix.png differ diff --git a/6.2.X/administration/assets/notifier-connectors.png b/6.2.X/administration/assets/notifier-connectors.png new file mode 100755 index 00000000..e8814520 Binary files /dev/null and b/6.2.X/administration/assets/notifier-connectors.png differ diff --git a/6.2.X/administration/assets/observable_related_to_entities.png b/6.2.X/administration/assets/observable_related_to_entities.png new file mode 100755 index 00000000..f88e47c8 Binary files /dev/null and b/6.2.X/administration/assets/observable_related_to_entities.png differ diff --git a/6.2.X/administration/assets/observables_propagation.png b/6.2.X/administration/assets/observables_propagation.png new file mode 100755 index 00000000..30a4c8e0 Binary files /dev/null and b/6.2.X/administration/assets/observables_propagation.png differ diff --git a/6.2.X/administration/assets/organisation_via_participation.png b/6.2.X/administration/assets/organisation_via_participation.png new file mode 100755 index 00000000..a552df34 Binary files /dev/null and b/6.2.X/administration/assets/organisation_via_participation.png differ diff --git a/6.2.X/administration/assets/organization_admin_view.png b/6.2.X/administration/assets/organization_admin_view.png new file mode 100755 index 00000000..3d4f72c2 Binary files /dev/null and b/6.2.X/administration/assets/organization_admin_view.png differ diff --git a/6.2.X/administration/assets/parameters_broadcast_message_dismissible.png b/6.2.X/administration/assets/parameters_broadcast_message_dismissible.png new file mode 100755 index 00000000..ee46654c Binary files /dev/null and b/6.2.X/administration/assets/parameters_broadcast_message_dismissible.png differ diff --git a/6.2.X/administration/assets/parameters_broadcast_message_non-dismissible.png b/6.2.X/administration/assets/parameters_broadcast_message_non-dismissible.png new file mode 100755 index 00000000..52313bff Binary files /dev/null and b/6.2.X/administration/assets/parameters_broadcast_message_non-dismissible.png differ diff --git a/6.2.X/administration/assets/parameters_configuration.png b/6.2.X/administration/assets/parameters_configuration.png new file mode 100755 index 00000000..9ec9cde6 Binary files /dev/null and b/6.2.X/administration/assets/parameters_configuration.png differ diff --git a/6.2.X/administration/assets/parameters_platform.png b/6.2.X/administration/assets/parameters_platform.png new file mode 100755 index 00000000..3cdb3f85 Binary files /dev/null and b/6.2.X/administration/assets/parameters_platform.png differ diff --git a/6.2.X/administration/assets/parameters_theme_customization.png b/6.2.X/administration/assets/parameters_theme_customization.png new file mode 100755 index 00000000..56413408 Binary files /dev/null and b/6.2.X/administration/assets/parameters_theme_customization.png differ diff --git a/6.2.X/administration/assets/parameters_tools.png b/6.2.X/administration/assets/parameters_tools.png new file mode 100755 index 00000000..4336ecb8 Binary files /dev/null and b/6.2.X/administration/assets/parameters_tools.png differ diff --git a/6.2.X/administration/assets/platform-main-organization.png b/6.2.X/administration/assets/platform-main-organization.png new file mode 100755 index 00000000..bfe05c12 Binary files /dev/null and b/6.2.X/administration/assets/platform-main-organization.png differ diff --git a/6.2.X/administration/assets/platform_banner.png b/6.2.X/administration/assets/platform_banner.png new file mode 100755 index 00000000..e7bb739a Binary files /dev/null and b/6.2.X/administration/assets/platform_banner.png differ diff --git a/6.2.X/administration/assets/platform_message_examples.png b/6.2.X/administration/assets/platform_message_examples.png new file mode 100755 index 00000000..2711f7a5 Binary files /dev/null and b/6.2.X/administration/assets/platform_message_examples.png differ diff --git a/6.2.X/administration/assets/rbac.png b/6.2.X/administration/assets/rbac.png new file mode 100755 index 00000000..1f87e6db Binary files /dev/null and b/6.2.X/administration/assets/rbac.png differ diff --git a/6.2.X/administration/assets/retention-policy-parameters.png b/6.2.X/administration/assets/retention-policy-parameters.png new file mode 100755 index 00000000..131c8d48 Binary files /dev/null and b/6.2.X/administration/assets/retention-policy-parameters.png differ diff --git a/6.2.X/administration/assets/sighting_from_indicator.png b/6.2.X/administration/assets/sighting_from_indicator.png new file mode 100755 index 00000000..73a07dd5 Binary files /dev/null and b/6.2.X/administration/assets/sighting_from_indicator.png differ diff --git a/6.2.X/administration/assets/sighting_from_observable.png b/6.2.X/administration/assets/sighting_from_observable.png new file mode 100755 index 00000000..94c8849b Binary files /dev/null and b/6.2.X/administration/assets/sighting_from_observable.png differ diff --git a/6.2.X/administration/assets/sighting_via_obs_data.png b/6.2.X/administration/assets/sighting_via_obs_data.png new file mode 100755 index 00000000..42d680b7 Binary files /dev/null and b/6.2.X/administration/assets/sighting_via_obs_data.png differ diff --git a/6.2.X/administration/assets/status_templates.png b/6.2.X/administration/assets/status_templates.png new file mode 100755 index 00000000..a6589ff5 Binary files /dev/null and b/6.2.X/administration/assets/status_templates.png differ diff --git a/6.2.X/administration/assets/support-package-overview.png b/6.2.X/administration/assets/support-package-overview.png new file mode 100755 index 00000000..baab6cd2 Binary files /dev/null and b/6.2.X/administration/assets/support-package-overview.png differ diff --git a/6.2.X/administration/assets/system_settings.png b/6.2.X/administration/assets/system_settings.png new file mode 100755 index 00000000..448e25c2 Binary files /dev/null and b/6.2.X/administration/assets/system_settings.png differ diff --git a/6.2.X/administration/assets/targeting_via_attribution.png b/6.2.X/administration/assets/targeting_via_attribution.png new file mode 100755 index 00000000..247eeee7 Binary files /dev/null and b/6.2.X/administration/assets/targeting_via_attribution.png differ diff --git a/6.2.X/administration/assets/targeting_via_belonging.png b/6.2.X/administration/assets/targeting_via_belonging.png new file mode 100755 index 00000000..bbb51d38 Binary files /dev/null and b/6.2.X/administration/assets/targeting_via_belonging.png differ diff --git a/6.2.X/administration/assets/targeting_via_location.png b/6.2.X/administration/assets/targeting_via_location.png new file mode 100755 index 00000000..6565f8da Binary files /dev/null and b/6.2.X/administration/assets/targeting_via_location.png differ diff --git a/6.2.X/administration/assets/targeting_via_sighting.png b/6.2.X/administration/assets/targeting_via_sighting.png new file mode 100755 index 00000000..76ffff36 Binary files /dev/null and b/6.2.X/administration/assets/targeting_via_sighting.png differ diff --git a/6.2.X/administration/assets/targeting_when_located.png b/6.2.X/administration/assets/targeting_when_located.png new file mode 100755 index 00000000..89a0bee4 Binary files /dev/null and b/6.2.X/administration/assets/targeting_when_located.png differ diff --git a/6.2.X/administration/assets/teams-notifier-sample.png b/6.2.X/administration/assets/teams-notifier-sample.png new file mode 100755 index 00000000..211f8c28 Binary files /dev/null and b/6.2.X/administration/assets/teams-notifier-sample.png differ diff --git a/6.2.X/administration/assets/tlp-diagram.png b/6.2.X/administration/assets/tlp-diagram.png new file mode 100755 index 00000000..f970b012 Binary files /dev/null and b/6.2.X/administration/assets/tlp-diagram.png differ diff --git a/6.2.X/administration/assets/update-group-new.png b/6.2.X/administration/assets/update-group-new.png new file mode 100755 index 00000000..6f59ee81 Binary files /dev/null and b/6.2.X/administration/assets/update-group-new.png differ diff --git a/6.2.X/administration/assets/update-role.png b/6.2.X/administration/assets/update-role.png new file mode 100755 index 00000000..39e353be Binary files /dev/null and b/6.2.X/administration/assets/update-role.png differ diff --git a/6.2.X/administration/assets/usage_propagation.png b/6.2.X/administration/assets/usage_propagation.png new file mode 100755 index 00000000..a7c7ee34 Binary files /dev/null and b/6.2.X/administration/assets/usage_propagation.png differ diff --git a/6.2.X/administration/assets/user-manage.png b/6.2.X/administration/assets/user-manage.png new file mode 100755 index 00000000..13a79158 Binary files /dev/null and b/6.2.X/administration/assets/user-manage.png differ diff --git a/6.2.X/administration/assets/user-overveiw-new.png b/6.2.X/administration/assets/user-overveiw-new.png new file mode 100755 index 00000000..86e64bb7 Binary files /dev/null and b/6.2.X/administration/assets/user-overveiw-new.png differ diff --git a/6.2.X/administration/assets/vocabularies.png b/6.2.X/administration/assets/vocabularies.png new file mode 100755 index 00000000..f1f45642 Binary files /dev/null and b/6.2.X/administration/assets/vocabularies.png differ diff --git a/6.2.X/administration/assets/workflow-configuration.png b/6.2.X/administration/assets/workflow-configuration.png new file mode 100755 index 00000000..c16f5238 Binary files /dev/null and b/6.2.X/administration/assets/workflow-configuration.png differ diff --git a/6.2.X/administration/audit/configuration/index.html b/6.2.X/administration/audit/configuration/index.html new file mode 100755 index 00000000..ac70bd92 --- /dev/null +++ b/6.2.X/administration/audit/configuration/index.html @@ -0,0 +1,4900 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configuration - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Configuration

+
+

Enterprise edition

+

Activity unified interface and logging are available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all information

+
+

As explained in the overview page, all administration actions are listened by default. +However, all knowledge are not listened by default due to performance impact on the platform.

+

For this reason you need to explicitly activate extended listening on user / group or organization.

+

Audit interface

+

Listening will start just after the configuration. Every past events will not be taken into account.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/audit/events/index.html b/6.2.X/administration/audit/events/index.html new file mode 100755 index 00000000..3c646fd0 --- /dev/null +++ b/6.2.X/administration/audit/events/index.html @@ -0,0 +1,4986 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Events - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Events

+
+

Enterprise edition

+

Activity unified interface and logging are available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all information

+
+

Description

+

OpenCTI activity capability is the way to unified whats really happen in the platform. +In events section you will have access to the UI that will answer to "who did what, where, and when?" within your data with the maximum level of transparency.

+

Audit interface

+

Include knowledge

+

By default, the events screen only show you the administration actions done by the users.

+

If you want to see also the information about the knowledge, you can simply activate the filter in the bar to get the complete overview of all user actions.

+

Don't hesitate to read again the overview page to have a better understanding of the difference between Audit, Basic/Extended knowledge.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/audit/overview/index.html b/6.2.X/administration/audit/overview/index.html new file mode 100755 index 00000000..5d1ce92d --- /dev/null +++ b/6.2.X/administration/audit/overview/index.html @@ -0,0 +1,5761 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Overview

+ +

Overview

+
+

Enterprise edition

+

Activity unified interface and logging are available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all the information

+
+

OpenCTI activity capability is the way to unify what's really happening in the platform. +With this feature you will be able to answer "who did what, where, and when?" within your data with the maximum level of transparency.

+

Enabling activity helps your security, auditing, and compliance entities monitor platform for possible vulnerabilities or external data misuse.

+

Categories

+

The activity groups 3 different concepts that need to be explained.

+

Basic knowledge

+

The basic knowledge refers to all STIX data knowledge inside OpenCTI. Every create/update/delete action on that knowledge is accessible through the history. That basic activity is handled by the history manager and can also be found directly on each entity.

+

Extended knowledge

+

The extended knowledge refers to extra information data to track specific user activity. As this kind of tracking is expensive, the tracking will only be done for specific users/groups/organizations explicitly configured in the configuration window.

+

Audit knowledge

+

Audit is focusing on user administration or security actions. +Audit will produce console/logs files along with user interface elements.

+
{
+  "auth": "<User information>",
+  "category": "AUDIT",
+  "level": "<info | error>",
+  "message": "<human readable explanation>",
+  "resource": {
+    "type": "<authentication | mutation>",
+    "event_scope": "<depends on type>",
+    "event_access": "<administration>",
+    "data": "<contextual data linked to the event type>",
+    "version": "<version of audit log format>"
+  },
+  "timestamp": "<event date>",
+  "version": "<platform version>"
+}
+
+

Architecture

+

OpenCTI uses different mechanisms to be able to publish actions (audit) or data modification (history)

+ + +

Audit knowledge

+
+

Administration or security actions

+

With Enterprise edition activated, Administration and security actions are always written; you can't configure, exclude, or disable them

+

✅ Supported

+

❌ Not supported for now

+

🚫 Not applicable

+
+

Ingestion

+ + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Remote OCTI Streams✅✅✅
+

Data sharing

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CreateDeleteEdit
CSV Feeds✅✅✅
TAXII Feeds✅✅✅
Stream Feeds✅✅✅
+

Connectors

+ + + + + + + + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Connectors✅✅✅ State reset
Works🚫✅🚫
+

Parameters

+ + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Platform parameters🚫🚫✅
+

Security

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Roles✅✅✅
Groups✅✅✅
Users✅✅✅
Sessions🚫✅🚫
Policies🚫🚫✅
+

Customization

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Entity types🚫🚫✅
Rules engine🚫🚫✅
Retention policies✅✅✅
+

Taxonomies

+ + + + + + + + + + + + + + + + + + + + + + + +
CreateDeleteEdit
Status templates✅✅✅
Case templates + tasks✅✅✅
+

Accesses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Listen
Login (success or fail)✅
Logout✅
Unauthorized access✅
+

Extended knowledge

+
+

Extended knowledge

+

Extented knowledge activity are written only if you activate the feature for a subset of users / groups or organizations

+
+

Data management

+

Some history actions are already included in the "basic knowledge". (basic marker)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReadCreateDeleteEdit
Platform knowledge✅basicbasicbasic
Background tasks knowledge🚫✅✅🚫
Knowledge files✅basicbasic🚫
Global data import files✅✅✅🚫
Analyst workbenches files🚫✅✅🚫
Triggers🚫✅✅❌
Workspaces✅✅✅❌
Investigations✅✅✅❌
User profile🚫🚫🚫✅
+

User actions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Supported
Ask for file import✅
Ask for data enrichment✅
Ask for export generation✅
Execute global search✅
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/audit/triggers/index.html b/6.2.X/administration/audit/triggers/index.html new file mode 100755 index 00000000..c50bebda --- /dev/null +++ b/6.2.X/administration/audit/triggers/index.html @@ -0,0 +1,5049 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Activity triggers - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Activity triggers

+
+

Enterprise edition

+

Activity unified interface and logging are available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all information

+
+

Description

+

Having all the history in the user interface (events) is sometimes not enough to have a proactive monitoring. For this reason, you can configure some specific triggers to receive notifications on audit events. +You can configure like personal triggers, lives one that will be sent directly or digest depending on your needs.

+

Configuration

+

In this type of trigger, you will need to configure various options:

+
    +
  • Notification target: user interface or email,
  • +
  • Recipients: the recipient of the notification,
  • +
  • Filters: a set of filters to get only events that really interest you.
  • +
+

Event structure

+

In order to correctly configure the filters, here's a definition of the event structure

+
    +
  • +

    Event type: authentication

    +
      +
    • Event scopes: login and logout
    • +
    +
  • +
  • +

    Event type: read

    +
      +
    • Event scopes: read and unauthorized
    • +
    +
  • +
  • +

    Event type: file

    +
      +
    • Event scopes: read, create and delete
    • +
    +
  • +
  • +

    Event type: mutation

    +
      +
    • Event scopes: unauthorized, update, create and delete
    • +
    +
  • +
  • +

    Event type: command

    +
      +
    • Event scopes: search, enrich, import and export
    • +
    +
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/csv-mappers/index.html b/6.2.X/administration/csv-mappers/index.html new file mode 100755 index 00000000..571a2be3 --- /dev/null +++ b/6.2.X/administration/csv-mappers/index.html @@ -0,0 +1,5258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSV Mappers - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

CSV Mappers

+

In OpenCTI, CSV Mappers allow to parse CSV files in a STIX 2.1 Object. The mappers are created and configured by users with the Manage CSV mapper capability. Then, they are available to users who import CSV files, for instance inside a report or in the global import view.

+

Principles

+

The mapper contains representations of STIX 2.1 entities and relationships, in order for the parser to properly extract them. One mapper is dedicated to parsing a specific CSV file structure, and thus dedicated mappers should be created for every specific CSV structure you might need to ingest in the platform.

+

Create a new CSV Mapper

+

In menu Data, select the submenu Processing, and on the right menu select CSV Mappers. You are presented with a list of all the mappers set in the platform. Note that you can delete or update any mapper from the context menu via the burger button beside each mapper.

+

Click on the button + in the bottom-right corner to add a new Mapper.

+

Enter a name for your mapper and some basic information about your CSV files:

+
    +
  • The line separator used (defaults to the standard comma character)
  • +
  • The presence of a header on the first line
  • +
+
+

Header management

+

The parser will not extract any information from the CSV header if any, it will just skip the first line during parsing.

+
+

Creating a new CSV Mapper

+

Then, you need to create every representation, one per entity and relationship type represented in the CSV file. +Click on the + button to add an empty representation in the list, and click on the chevron to expand the section and configure the representation.

+

Depending on the entity type, the form contains the fields that are either required (input outlined in red) or optional. +For each field, set the corresponding columns mapping (the letter-based index of the column in the CSV table, as presented in common spreadsheet tools).

+

References to other entities should be picked from the list of all the other representations already defined earlier in the mapper.

+

You can do the same for all the relationships between entities that might be defined in this particular CSV file structure.

+

New representation

+

Fields might have options besides the mandatory column index, to help extract relevant data:

+
    +
  • Date values are expected in ISO 8601 format, but you can set your own format to the time parser
  • +
  • Multiple values can be extracted by specifying the separator used inside the cell (e.g. + or |)
  • +
+

Or to set default values in case some data is missing in the imported file.

+

Field options

+

CSV Mapper validity

+

The only parameter required to save a CSV Mapper is a name. The creation and refinement of its representations can be done iteratively.

+

Nonetheless, all CSV Mappers go through a quick validation that checks if all the representations have all their mandatory fields set. +Only valid mappers can be run by the users on their CSV files.

+

Mapper validity is visible in the list of CSV Mappers as shown below.

+

An invalid CSV Mapper

+

Test your CSV mapper

+

In the creation or edition form, hit the button Test to open a dialog. Select a sample CSV file and hit the Test button.

+

The code block contains the raw result of the parsing attempt, in the form of a STIX 2.1 bundle in JSON format.

+

You can then check if the extracted values match the expected entities and relationships.

+

Test a CSV Mapper

+
+

Partial test

+

The test conducted in this window relies only on the translation of CSV data according to the chosen representation in the mapper. It does not take into account checks for accurate entity formatting (e.g. IPv4) or specific entity configurations (e.g. mandatory "description" field on reports). Consequently, the entities visible in the test window may not be created during the actual import process.

+
+
+

Test with a small file

+

We strongly recommend limiting test files to 100 lines and 1MB. Otherwise, the browser may crash.

+
+

Use a mapper for importing a CSV file

+

You can change the default configuration of the import csv connector in your configuration file.

+
"import_csv_built_in_connector": {
+  "enabled": true, 
+  "interval": 10000, 
+  "validate_before_import": false
+},
+
+

In Data import section, or Data tab of an entity, when you upload a CSV, you can select a mapper to apply to the file. +The file will then be parsed following the representation rules set in the mapper.

+

By default, the imported elements will be added in a new Analyst Workbench where you will be able to check the result of the import.

+

Default values for attributes

+

In the case of the CSV file misses some data, you can complete it with default values. To achieve this, you have two possibilities:

+
    +
  • Set default values in the settings of the entities,
  • +
  • Set default values directly in the CSV mapper.
  • +
+

Set default values in the settings of the entities

+
+

Default value mechanisms

+

Note that adding default values in settings have an impact at entity creation globally on the platform, not only on CSV mappers. If you want to apply those default values only at CSV mapper level, please use the second option.

+
+

In settings > Customization, you can select an entity type and then set default values for its attributes.

+

mapper-settings

+

In the configuration of the entity, you have access to the entity's attributes that can be managed.

+

Click on the attribute to add a default value information.

+

mapper-custom

+

Enter the default value in the input and save the update.

+

mapper-attribute

+

The value filled will be used in the case where the CSV file lacks data for this attribute.

+

Set specific default values directly in the CSV mapper

+
+

Information retained in case of default value

+

If you fill a default value in entity settings and the CSV mapper, the one from CSV mapper will be used.

+
+

In the mapper form, you will see next to the column index input a gear icon to add extra information for the attribute. If the attribute can have a customizable default value, then you will be able to set one here.

+

mapper-attribute

+

The example above shows the case of the attribute architecture implementation of a malware. You have some information here. First, it seems we have a default value already set in entity settings for this attribute with the value [powerpc, x86]. However, we want to override this value with another one for our case: [alpha].

+

Specific case of marking definitions

+

For marking definitions, setting a default value is different from other attributes. We are not choosing a particular marking definition to use if none is specified in the CSV file. Instead, we will choose a default policy. Two option are available:

+
    +
  • Use the default marking definitions of the user. In this case the default marking definitions of the connected user importing the CSV file will be used,
  • +
  • Let the user choose marking definitions. Here the user importing the CSV file will choose marking definitions (among the ones they can see) when selecting the CSV mapper.
  • +
+

mapper-attribute

+

Additional resources

+
    +
  • Usefulness: To additional content on entity customization, refers to the Customize entities page in the Administration section of the documentation.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/decay-rules/index.html b/6.2.X/administration/decay-rules/index.html new file mode 100755 index 00000000..9b613b9b --- /dev/null +++ b/6.2.X/administration/decay-rules/index.html @@ -0,0 +1,4981 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Decay rules - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Decay rules

+

Decay rules are used to update automatically indicators score in order to represent their lifecycle.

+

Configuration

+

Decay rules can be configured in the "Settings > Customization > Decay rule" menu.

+

Decay rules

+

There are built-in decay rules that can't be modified and are applied by default to indicators depending on their main observable type. +Decay rules are applied from highest to lowest order (the lowest being 0).

+

You can create new decay rules with higher order to apply them along with (or instead of) the built-in rules.

+

Decay rule creation

+

When you create a decay rule, you can specify on which indicators' main observable types it will apply. If you don't enter any, it will apply to all indicators.

+

You can also add reaction points which represent the scores at which indicators are updated. For example, if you add one reaction point at 60 and another one at 40, indicators that have an initial score of 80 will be updated with a score of 60, then 40, depending on the decay curve.

+

The decay curve is based on two parameters:

+
    +
  • the decay factor, which represents the speed at which the score falls, and
  • +
  • the lifetime, which represents the time (in days) during which the value will be lowered until it reaches 0.
  • +
+

Finally, the revoke score is the score at which the indicator can be revoked automatically.

+

Decay rule creation filled

+

Once you have created a new decay rule, you will be able to view its details, along with a life curve graph showing the score evolution over time.

+

You will also be able to edit your rule, change all its parameters and order, activate or deactivate it (only activated rules are applied), or delete it.

+

Decay rule created

+
+

Indicator decay manager

+

Decay rules are only applied, and indicators score updated, if indicator decay manager is enabled (enabled by default).

+

Please read the dedicated page to have all information

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/enterprise/index.html b/6.2.X/administration/enterprise/index.html new file mode 100755 index 00000000..92c8ee71 --- /dev/null +++ b/6.2.X/administration/enterprise/index.html @@ -0,0 +1,5126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enterprise edition - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Enterprise edition

+ +
+

Filigran

+

Filigran is providing an Enterprise Edition of the platform, whether on-premise or in the SaaS.

+
+

What is OpenCTI EE?

+

OpenCTI Enterprise Edition is based on the open core concept. This means that the source code of OCTI EE remains open source and included in the main GitHub repository of the platform but is published under a specific license. As specified in the GitHub license file:

+
    +
  • The OpenCTI Community Edition is licensed under the Apache License, Version 2.0 (the “Apache License”).
  • +
  • The OpenCTI Enterprise Edition is licensed under the OpenCTI Enterprise Edition License (the “Enterprise Edition Licensee”).
  • +
+

The source files in this repository have a header indicating which license they are under. If no such header is provided, this means that the file belongs to the Community Edition under the Apache License, Version 2.0.

+

We wrote a complete article to explain the enterprise edition, feel free to read it to have more information

+

EE Activation

+

Enterprise edition is easy to activate. You need to go the platform settings and click on the Activate button.

+

OpenCTI activation

+

Then you will need to agree to the Filigran EULA.

+

OpenCTI EE EULA

+

As a reminder:

+
    +
  • OpenCTI EE is free-to-use for development, testing and research purposes as well as for non-profit organizations.
  • +
  • OpenCTI EE is included for all Filigran SaaS customers without additional fee.
  • +
  • For all other usages, OpenCTI EE is reserved to organizations that have signed a Filigran Enterprise agreement.
  • +
+

Available features

+

Activity monitoring

+

Audit logs help you answer "who did what, where, and when?" within your data with the maximum level of transparency. Please read Activity monitoring page to get all information.

+

Playbooks and automation

+

OpenCTI playbooks are flexible automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform. Please read Playbook automation page to get all information.

+

Organizations management and segregation

+

Organizations segregation is a way to segregate your data considering the organization associated to the users. Useful when your platform aims to share data to multiple organizations that have access to the same OpenCTI platform. Please read Organizations RBAC to get more information.

+

Full text indexing

+

Full text indexing grants improved searches across structured and unstructured data. OpenCTI classic searches are based on metadata fields (e.g. title, description, type) while advanced indexing capability enables searches to be extended to the document’s contents. Please read File indexing to get all information.

+

More to come

+

More features will be available in OpenCTI in the future. Features like:

+
    +
  • Generative AI for correlation and content generation.
  • +
  • Supervised machine learning for natural language processing.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/entities/index.html b/6.2.X/administration/entities/index.html new file mode 100755 index 00000000..71920d98 --- /dev/null +++ b/6.2.X/administration/entities/index.html @@ -0,0 +1,5145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Entity types - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Customize entities

+

Introduction

+

A variety of entity customization options are available to optimize data representation, workflow management, and enhance overall user experience. Whether you're fine-tuning processing statuses, configuring entities' attributes, or hiding entities, OpenCTI's customization capabilities provide the flexibility you need to create a tailored environment for your threat intelligence and cybersecurity workflows.

+

The following chapter aims to provide readers with an understanding of the available customization options by entity type. Customize entities can be done in "Settings > Customization".

+

Customization menu

+

Hidden in interface

+

This configuration allows to hide a specific entity type throughout the entire platform. It provides a potent means to simplify the interface and tailor it to your domain expertise. For instance, if you have no interest in disinformation campaigns, you can conceal related entities such as Narratives and Channels from the menus.

+

You can specify which entities to hide on a platform-wide basis from "Settings > Customization" and from "Settings > Parameters", providing you with a list of hidden entities. Furthermore, you can designate hidden entities for specific Groups and Organizations from "Settings > Security > Groups/Organizations" by editing a Group/Organization.

+

An overview of hidden entity types is available in the "Hidden entity types" field in "Settings > Parameters."

+

Automatic references at file upload

+

This configuration enables an entity to automatically construct an external reference from the uploaded file.

+

Enforce references

+

This configuration enables the requirement of a reference message on an entity creation or modification. This option is helpful if you want to keep a strong consistency and traceability of your knowledge and is well suited for manual creation and update.

+

Entity configuration

+

+

Workflow

+

For now, OpenCTI has a simple workflow approach. They're represented by the "Processing status" field embedded in each object. By default, this field is disabled for most objects but can be activated through the platform settings:

+
    +
  1. Navigate to "Settings > Customization > Entity types > [Desired object type]."
  2. +
  3. Click on the small pink pen icon next to "Workflow" to access the object customization window.
  4. +
  5. Add and configure the desired statuses, defining their order within the workflow.
  6. +
+

Workflow status

+

In addition, the available statuses are defined by a collection of status templates visible in "Settings > Taxonomies > Status templates". This collection can be customized.

+

+

Attributes

+

Each attribute in an Entity offers several customization options:

+
    +
  • It can be set as mandatory if not already defined as such in the STIX standard.
  • +
  • A default value can be established to streamline entity creation through the creation forms.
  • +
  • Different thresholds and their corresponding labels for scalable attributes can be defined.
  • +
+

Mandatory attributes and default values

+

Confidence scale configuration

+

Confidence scale can be customized for each entity type by selecting another scale template or by editing directly the scale values. +Once you have customized your scale, click on "Update" to save your configuration.

+

Mandatory attributes and default values

+
+

Max confidence level

+

The above scale also needs to take into account the confidence level per user. To understand the concept, please navigate to this page

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/file-indexing/index.html b/6.2.X/administration/file-indexing/index.html new file mode 100755 index 00000000..79d420a7 --- /dev/null +++ b/6.2.X/administration/file-indexing/index.html @@ -0,0 +1,4984 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File indexing - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

File indexing

+
+

Enterprise edition

+

Platform segregation by organization is available under the "OpenCTI Enterprise Edition" license. Please read the dedicated page to have all the information.

+
+

Files search requirements

+

In order to search in files, we need to extract and index files text content, which requires to have one of these database configurations :

+ +

File indexing configuration

+

File indexing can be configured via the File indexing tab in the Settings menu.

+

The configuration and impact panel shows all file types that can be indexed, as well as the volume of storage used.

+

It is also possible to include or exclude files uploaded from the global Data import panel and that are not associated with a specific entity in the platform.

+

Finally, it is possible to set a maximum file size for indexing (5 Mb by default).

+

Currently supported content types : application/pdf, text/plain, text/csv, text/html, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (excel sheets).

+

file-indexing-settings.png

+

Once indexing has been launched by pressing the Start button, you can follow its progress.

+

You will also be able to Pause it and restart from the beginning by pressing Reset button which deletes all indexed files from the database.

+

If you change the configuration while file indexing is running, you might need to reset in order to include newly impacted files.

+

File indexing runs every 5 minutes to index last uploaded files.

+

file-indexing-monitoring.png

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/introduction/index.html b/6.2.X/administration/introduction/index.html new file mode 100755 index 00000000..fb561f1d --- /dev/null +++ b/6.2.X/administration/introduction/index.html @@ -0,0 +1,4887 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Introduction

+

This guide aims to give you a full overview of the OpenCTI features and workflows. The platform can be used in various contexts to handle threats management use cases from a technical to a more strategic level.

+

Administrative Settings

+

The OpenCTI Administrative settings console allows administrators to configure many options dynamically within the system. As an Administrator, you can access this settings console, by clicking the settings link. +Settings Link

+

The Settings Console allows for configuration of various aspects of the system.

+

General Configuration

+
    +
  • Platform Title (Default: OpenCTI - Cyber Threat Intelligence Platform)
  • +
  • Platform Favicon
  • +
  • Platform General Sender email (Default: admin@opencti.io)
  • +
  • Platform Default Theme (Default: Dark)
  • +
  • Language (Default: Automatic Detection)
  • +
  • Hidden Entity Types (Default: None)
  • +
+

Authentication Strategies Display

+
    +
  • This section will show configured and enabled/disabled strategies. The configuration is done in the config/default.json file or via ENV variables detected at launch.
  • +
+

Platform Messages

+
    +
  • Platform Login Message (optional) - if configured this will be displayed on the login page. This is usually used to have a welcome type message for users before login.
  • +
  • Platform Consent Message (optional) - if configured this will be displayed on the login page. This is usually used to display some type of consent message for users to agree to before login. If enabled, a user must check the checkbox displayed to allow login.
  • +
  • Platform Consent Confirm Text (optional) - This is displayed next to the platform consent checkbox, if Platform Consent Message is configured. Users must agree to the checkbox before the login prompt will be displayed. This message can be configured, but by default reads: I have read and comply with the above statement
  • +
+

Platform Messages

+

Dark Theme Color Scheme

+

Various aspects of the Dark Theme can be dynamically configured in this section.

+

Light Theme Color Scheme

+

Various aspects of the Light Theme can be dynamically configured in this section.

+

Tools Configuration Display

+

This section will give general status on the various tools and enabled components of the currently configured OpenCTI deployment.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/merging/index.html b/6.2.X/administration/merging/index.html new file mode 100755 index 00000000..8919af88 --- /dev/null +++ b/6.2.X/administration/merging/index.html @@ -0,0 +1,5022 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Merging and de-duplication - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Merging

+

Data merging

+

Within the OpenCTI platform, the merge capability is present into the "Data > Entities" tab, and is fairly straightforward to use. To execute a merge, select the set of entities to be merged, then click on the Merge icon.

+
+

Merging limitation

+

It is not possible to merge entities of different types, nor is it possible to merge more than 4 entities at a time (it will have to be done in several stages).

+
+

Merge_panel

+

Central to the merging process is the selection of a main entity. This primary entity becomes the anchor, retaining crucial attributes such as name and description. Other entities, while losing specific fields like descriptions, are aliased under the primary entity. This strategic decision preserves vital data while eliminating redundancy.

+

Main_entity_selection

+

Once the choice has been made, simply validate to run the task in the background. Depending on the number of entity relationships, and the current workload on the platform, the merge may take more or less time. In the case of a healthy platform and around a hundred relationships per entity, merge is almost instantaneous.

+

Data preservation and relationship continuity

+

A common concern when merging entities lies in the potential loss of information. In the context of OpenCTI, this worry is alleviated. Even if the merged entities were initially created by distinct sources, the platform ensures that data is not lost. Upon merging, the platform automatically generates relationships directly on the merged entity. This strategic approach ensures that all connections, regardless of their origin, are anchored to the consolidated entity. Post-merge, OpenCTI treats these once-separate entities as a singular, unified entity. Subsequent information from varied sources is channeled directly into the entity resulting from the merger. This unified entity becomes the focal point for all future relationships, ensuring the continuity of data and relationships without any loss or fragmentation.

+

Important considerations

+
    +
  • Irreversible process: It's essential to know that a merge operation is irreversible. Once completed, the merged entities cannot be reverted to their original state. Consequently, careful consideration and validation are crucial before initiating the merge process.
  • +
  • Loss of fields in aliased entities: Fields, such as descriptions, in aliased entities - entities that have not been chosen as the main - will be lost during the merge. Ensuring that essential information is captured in the primary entity is crucial to prevent data loss.
  • +
+

Additional resources

+
    +
  • Usefulness: To understand the benefits of entity merger, refer to the Merge objects page in the User Guide section of the documentation.
  • +
  • Deduplication mechanism: the platform is equipped with deduplication processes that automatically merge data at creation (either manually or by importing data from different sources) if it meets certain conditions.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/notifier-samples/index.html b/6.2.X/administration/notifier-samples/index.html new file mode 100755 index 00000000..c10f1d0a --- /dev/null +++ b/6.2.X/administration/notifier-samples/index.html @@ -0,0 +1,5078 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notifier samples - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Notifier samples

+

Configure Teams webhook

+

To configure a notifier for Teams, allowing to send notifications via Teams messages, we followed the guidelines outlined in the Microsoft documentation.

+

Teams notifier sample

+

Template message for live trigger

+

The Teams template message sent through webhook for a live notification is:

+
{
+        "type": "message",
+        "attachments": [
+            {
+                "contentType": "application/vnd.microsoft.card.thumbnail",
+                "content": {
+                    "subtitle": "Operation : <%=content[0].events[0].operation%>",
+                    "text": "<%=(new Date(notification.created)).toLocaleString()%>",
+                    "title": "<%=content[0].events[0].message%>",
+                    "buttons": [
+                        {
+                            "type": "openUrl",
+                            "title": "See in OpenCTI",
+                            "value": "https://YOUR_OPENCTI_URL/dashboard/id/<%=content[0].events[0].instance_id%>"
+                        }
+                    ]
+                }
+            }
+        ]
+    }
+
+

Template message for digest

+

The Teams template message sent through webhook for a digest notification is:

+
{
+    "type": "message",
+    "attachments": [
+        {
+            "contentType": "application/vnd.microsoft.card.adaptive",
+            "content": {
+                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+                "type": "AdaptiveCard",
+                "version": "1.0",
+                "body": [
+                    {
+                        "type": "Container",
+                        "items": [
+                            {
+                                "type": "TextBlock",
+                                "text": "<%=notification.name%>",
+                                "weight": "bolder",
+                                "size": "extraLarge"
+                            }, {
+                                "type": "TextBlock",
+                                "text": "<%=(new Date(notification.created)).toLocaleString()%>",
+                                "size": "medium"
+                            }
+                        ]
+                    },
+                    <% for(var i=0; i<content.length; i++) { %>
+                    {
+                        "type": "Container",
+                        "items": [<% for(var j=0; j<content[i].events.length; j++) { %>
+                            {
+                                "type" : "TextBlock",
+                                "text" : "[<%=content[i].events[j].message%>](https://YOUR_OPENCTI_URL/dashboard/id/<%=content[i].events[j].instance_id%>)"
+                            }<% if(j<(content[i].events.length - 1)) {%>,<% } %>
+                        <% } %>]
+           }<% if(i<(content.length - 1)) {%>,<% } %>
+                    <% } %>
+                ]
+            }
+        }
+    ],
+   "dataString": <%-JSON.stringify(notification)%>
+}
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/notifiers/index.html b/6.2.X/administration/notifiers/index.html new file mode 100755 index 00000000..9303a679 --- /dev/null +++ b/6.2.X/administration/notifiers/index.html @@ -0,0 +1,5094 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Custom notifiers - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Custom notifiers

+

Leveraging the platform's built-in connectors, users can create custom notifiers tailored to their unique needs. OpenCTI features three built-in connectors: a webhook connector, a simple mailer connector, and a platform mailer connector. These connectors operate based on registered schemas that describe their interaction methods.

+

Notifier connectors

+

Built-In notifier connectors

+

Platform mailer

+

This notifier connector enables customization of notifications raised within the platform. It's simple to configure, requiring only:

+
    +
  • Title: The title for the platform notification.
  • +
  • Template: Specifies the message template for the notification.
  • +
+

Simple mailer

+

This notifier connector offers a straightforward approach to email notifications with simplified configuration options. Users can set:

+
    +
  • Title: The title for the email notification.
  • +
  • Header: Additional content to include at the beginning of the email.
  • +
  • Footer: Additional content to include at the end of the email.
  • +
  • Logo: The option to add a logo to the email.
  • +
  • Background Color: Customize the background color of the email.
  • +
+

Custom email notifier

+

Webhook

+

This notifier connector enables users to send notifications to external applications or services through HTTP requests. Users can specify:

+
    +
  • Verb: Specifies the HTTP method (GET, POST, PUT, DELETE).
  • +
  • URL: Defines the destination URL for the webhook.
  • +
  • Template: Specifies the message template for the notification.
  • +
  • Parameters and Headers: Customizable parameters and headers sent through the webhook request.
  • +
+

OpenCTI provides two notifier samples by default, designed to communicate with Microsoft Teams through a webhook. A documentation page providing details on these samples is available.

+

Configuration and access

+

Custom notifiers are manageable in the "Settings > Customization > Notifiers" window and can be restricted through Role-Based Access Control (RBAC). Administrators can control access, limiting usage to specific Users, Groups, or Organizations.

+

For guidance on configuring notification triggers and exploring the usages of notifiers, refer to the dedicated documentation page.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/ontologies/index.html b/6.2.X/administration/ontologies/index.html new file mode 100755 index 00000000..fd2e87e4 --- /dev/null +++ b/6.2.X/administration/ontologies/index.html @@ -0,0 +1,5065 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Taxonomies - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Taxonomies

+

Taxonomies in OpenCTI refer to the structured classification systems that help in organizing and categorizing cyber threat intelligence data. They play a crucial role in the platform by allowing analysts to systematically tag and retrieve information based on predefined categories and terms.

+

Along with the Customization page, these pages allow the administrator to customize the platform.

+

Labels

+

Labels in OpenCTI serve as a powerful tool for organizing, categorizing, and prioritizing data. Here’s how they can be used effectively:

+
    +
  1. Tagging and Categorization: Labels can be used to tag malware, incidents, or indicators (IOCs) with specific categories, making it easier to filter and search through large datasets.
  2. +
  3. Prioritization: By labeling threats based on their severity or impact, security analysts can prioritize their response efforts accordingly.
  4. +
  5. Correlation and Analysis: Labels help in correlating different pieces of intelligence. For example, if multiple indicators are tagged with the same label, it might indicate a larger campaign or a common adversary.
  6. +
  7. Automation and Integration: Labels can trigger automated workflows (also called playbooks) within OpenCTI. For instance, a label might automatically initiate further investigation or escalate an incident.
  8. +
  9. Reporting and Metrics: Labels facilitate the generation of reports and metrics, allowing organizations to track trends through dashboards, measure response effectiveness, and make data-driven decisions.
  10. +
  11. Sharing and Collaboration: When sharing intelligence with other organizations or platforms, labels provide a common language that helps in understanding the context and relevance of the shared data.
  12. +
+
+

Tip

+

In order to achieve effective data labeling methods, it is recommended to establish a clear and consistent criteria for your labeling and document them in a policy or guideline.

+
+

Kill chain phases

+

Kill chain phases are used in OpenCTI to structure and analyze the data related to cyber threats and attacks. They describe the stages of an attack from the perspective of the attacker and provide a framework for identifying, analysing and responding to threats.

+

OpenCTI supports the following kill chain models:

+
    +
  • Lockheed Martin Cyber Kill Chain
  • +
  • MITRE ATT&CK Framework (Entreprise, PRE, Mobile and ICS)
  • +
  • DISARM framework
  • +
+

You can add, edit, or delete kill chain phases in the settings page, and assign them to indicators, attack patterns, incidents, or courses of action in the platform. You can also filter the data by kill chain phase, and view the kill chain phases in a timeline or as a matrix.

+

MITRE ATT&CK matrix of a threat actor

+

Vocabularies

+

Open vocabularies are sets of terms and definitions that are agreed upon by the CTI community. They help to standardize the communication documentation of cyber threat information. +This section allows you to customize a set of available fields by adding vocabulary. Almost all of the drop-down menus available in the entities can be modified from this panel.

+

Open vocabularies in OpenCTI are mainly based on the STIX standard.

+

Vocabularies in OpenCTI

+

Status templates

+

Status templates are predefined statuses that can be assigned to different entities in OpenCTI, such as reports, incidents, or cases (incident responses, requests for information and requests for takedown).

+

They help to track the progress of the analysis and response activities by defining statuses that are used in the workflows.

+

Status templates in OpenCTI

+

Case templates

+

Customizable case templates help to streamline the process of creating cases with predefined lists of tasks.

+

Case templates in OpenCTI

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/organization-segregation/index.html b/6.2.X/administration/organization-segregation/index.html new file mode 100755 index 00000000..6bfb5712 --- /dev/null +++ b/6.2.X/administration/organization-segregation/index.html @@ -0,0 +1,4999 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Organization segregation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Organization segregation

+
+

Enterprise edition

+

Platform segregation by organization is available under the "OpenCTI Enterprise Edition" license. Please read the dedicated page to have all the information.

+
+ + + + + + +

Organization administration

+

Platform administrators can promote members of an organization as "Organization administrator". This elevated role grants them the necessary capabilities to create, edit and delete users from the corresponding Organization. Additionally, administrators have the flexibility to define a list of groups that can be granted to newly created members by the organization administrators. This feature simplifies the process of granting appropriate access and privileges to individuals joining the organization.

+

Organization admin Settings view

+

The platform administrator can promote/demote an organization admin through its user edition form.

+

Organization admin promoting/demoting

+
+

Organization admin rights

+

The "Organization admin" has restricted access to Settings. They can only manage the members of the organizations for which they have been promoted as "admins".

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/parameters/index.html b/6.2.X/administration/parameters/index.html new file mode 100755 index 00000000..ed2192c7 --- /dev/null +++ b/6.2.X/administration/parameters/index.html @@ -0,0 +1,5124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Parameters - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Parameters

+

Description

+

This part of the interface wil let you configure global platform settings, like title, favicon, etc.

+

It will also give you important information about the platform.

+

The "Configuration" section

+

parameters_configuration.png

+

This section allows the administrator to edit the following settings:

+
    +
  • Platform title
  • +
  • Platform favicon URL
  • +
  • Sender email address: email address displayed as sender when sending notifications. The technical sender is defined in the SMTP configuration.
  • +
  • Theme
  • +
  • Language
  • +
  • Hidden entity types: allows you to customize which types of entities you want to see or hide in the platform. This can help you focus on the relevant information and avoid cluttering the platform with unnecessary data.
  • +
+

OpenCTI Platform

+

parameters_platform

+

This is where the Enterprise edition can be enabled.

+

This section gives important information about the platform like the used version, the edition, the architecture mode (can be Standalone or Cluster) and the number used nodes.

+

Through the "Remove Filigran logos" toggle, the administrator has the option to hide the Filigran logo on the login page and the sidebar.

+

Platform Announcement

+

This section gives you the possibility to set and display Announcements in the platform. Those announcements will be visible to every user in the platform, on top of the interface.

+

They can be used to inform some of your users or all of important information, like a scheduled downtime, an incoming upgrade, or even to share important tips regarding the usage of the platform.

+

An Announcement can be accompanied by a "Dismiss” button. When clicked by a user, it makes the message disappear for this user.

+

parameters_broadcast_message_dismissible

+

This option can be deactivated to have a permanent announcement.

+

parameters_broadcast_message_non-dismissible +⚠️ Only one announcement is shown at a time, with priority given to dismissible ones. If there are no dismissible announcements, the most recent non-dismissible one is shown.

+

Third-party Analytics

+
+

Enterprise edition

+

Analytics is available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have more information

+
+

This is where you can configure analytics providers. At the moment only Google Analytics v4 is supported.

+

Theme customization

+

In this section, the administrator can customize the two OpenCTI themes +parameters_theme_customization

+

Tools

+

This section informs the administrator of the statuses of the different managers used in the Platform. More information about the managers can be found here. +It shows also the used versions of the search engine database, RabbitMQ and Redis.

+

In cluster mode, the fact that a manager appears as enabled means that it is active in at least one node.

+

parameters_tools

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/policies/index.html b/6.2.X/administration/policies/index.html new file mode 100755 index 00000000..7c3b5e44 --- /dev/null +++ b/6.2.X/administration/policies/index.html @@ -0,0 +1,5131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Policies - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Policies

+

The Policies configuration window (in "Settings > Security > Policies") encompasses essential settings that govern the organizational sharing, authentication strategies, password policies, login messages, and banner appearance within the OpenCTI platform.

+

Platform main organization

+

Allow to set a main organization for the entire platform. Users belonging to the main organization enjoy unrestricted access to all data stored in the platform. In contrast, users affiliated with other organizations will only have visibility into data explicitly shared with them.

+

Platform main organization

+
+

Numerous repercussions linked to the activation of this feature

+

This feature has implications for the entire platform and must be fully understood before being used. For example, it's mandatory to have organizations set up for each user, otherwise they won't be able to log in. It is also advisable to include connector's users in the platform main organization to avoid import problems.

+
+

Authentication strategies

+

The authentication strategies section provides insights into the configured authentication methods. Additionally, an "Enforce Two-Factor Authentication" button is available, allowing administrators to mandate 2FA activation for users, enhancing overall account security.

+

Please see the Authentication section for further details on available authentication strategies.

+

Authentication strategies

+

Local password policies

+

This section encompasses a comprehensive set of parameters defining the local password policy. Administrators can specify requirements such as minimum/maximum number of characters, symbols, digits, and more to ensure robust password security across the platform. Here are all the parameters available:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescription
Number of chars must be greater than or equals toDefine the minimum length required for passwords.
Number of chars must be lower or equals to (0 equals no maximum)Set an upper limit for password length.
Number of symbols must be greater or equals toSpecify the minimum number of symbols required in a password.
Number of digits must be greater or equals toSet the minimum number of numeric characters in a password.
Number of words (split on hyphen, space) must be greater or equals toEnforce a minimum count of words in a password.
Number of lowercase chars must be greater or equals toSpecify the minimum number of lowercase characters.
Number of uppercase chars must be greater or equals toSpecify the minimum number of uppercase characters.
+

Local password policies

+

Login messages

+

Allow to define messages on the login page to customize and highlight your platform's security policy. Three distinct messages can be customized:

+
    +
  • Platform login message: Appears above the login form to convey important information or announcements.
  • +
  • Platform consent message: A consent message that obscures the login form until users check the approval box, ensuring informed user consent.
  • +
  • Platform consent confirm text: A message accompanying the consent box, providing clarity on the consent confirmation process.
  • +
+

Login message configuration

+

Login message illustration

+

Platform banner configuration

+

The platform banner configuration section allows administrators to display a custom banner message at the top and bottom of the screen. This feature enables customization for enhanced visual communication and branding within the OpenCTI platform. It can be used to add a disclaimer or system purpose.

+

This configuration has two parameters:

+
    +
  • Platform banner level: Options defining the banner background color (Green, Red, or Yellow).
  • +
  • Platform banner text: Field referencing the message to be displayed within the banner.
  • +
+

Platform Banner

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/reasoning/index.html b/6.2.X/administration/reasoning/index.html new file mode 100755 index 00000000..cbf00d5d --- /dev/null +++ b/6.2.X/administration/reasoning/index.html @@ -0,0 +1,5784 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rules engine - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Rules engine

+

Inference rules

+

The rules engine comprises a set of predefined rules (named inference rules) that govern how new relationships are inferred based on existing data. These rules are carefully crafted to ensure logical and accurate relationship creation. Here is the list of existing inference rules:

+

Raise incident based on sighting

+ + + + + + + + + + + + + +
ConditionsCreations
A non-revoked Indicator is sighted in an EntityCreation of an Incident linked to the sighted Indicator and the targeted Entity
+

Incident_based_on_sighting

+

Sightings of observables via observed data

+ + + + + + + + + + + + + +
ConditionsCreations
An Indicator is based on an Observable contained in an Observed DataCreation of a sighting between the Indicator and the creating Identity of the Observed Data
+

Sighting_via_observed_data

+

Sightings propagation from indicator

+ + + + + + + + + + + + + +
ConditionsCreations
An Indicator based on an Observable is sighted in an EntityThe Observable is sighted in the Entity
+

Sighting_prop_from_indicator

+

Sightings propagation from observable

+ + + + + + + + + + + + + +
ConditionsCreations
An Indicator is based on an Observable sighted in an EntityThe Indicator is sighted in the Entity
+

Sighting_prop_from_observable

+

Relation propagation via an observable

+ + + + + + + + + + + + + +
ConditionsCreations
An observable is related to two EntitiesCreate a related to relationship between the two Entities
+

Observable_related_to_entities

+

Attribution propagation

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A is attributed to an Entity B and this Entity B is itself attributed to an Entity CThe Entity A is attributed to Entity C
+

Attribution_propagation

+

Belonging propagation

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A is part of an Entity B and this Entity B is itself part of an Entity CThe Entity A is part of Entity C
+

Belonging_propagation

+

Location propagation

+ + + + + + + + + + + + + +
ConditionsCreations
A Location A is located at a Location B and this Location B is itself located at a Location CThe Location A is located at Location C
+

Location_propagation

+

Organization propagation via participation

+ + + + + + + + + + + + + +
ConditionsCreations
A User is affiliated with an Organization B, which is part of an Organization CThe User is affiliated to the Organization C
+

Organisation_via_participation

+

Identities propagation in reports

+ + + + + + + + + + + + + +
ConditionsCreations
A Report contains an Identity B and this Identity B is part of an Identity CThe Report contains Identity C, as well as the Relationship between Identity B and Identity C
+

Identities_propagation

+

Locations propagation in reports

+ + + + + + + + + + + + + +
ConditionsCreations
A Report contains a Location B and this Location B is located at a Location CThe Report contains Location B, as well as the Relationship between Location B and Location C
+

Locations_propagation_report

+

Observables propagation in reports

+ + + + + + + + + + + + + +
ConditionsCreations
A Report contains an Indicator and this Indicator is based on an ObservableThe Report contains the Observable, as well as the Relationship between the Indicator and the Observable
+

Observables_propagation

+

Usage propagation via attribution

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A, attributed to an Entity C, uses an Entity BThe Entity C uses the Entity B
+

Usage_propagation

+

Inference of targeting via a sighting

+ + + + + + + + + + + + + +
ConditionsCreations
An Indicator, sighted at an Entity C, indicates an Entity BThe Entity B targets the Entity C
+

Targeting_via_sighting

+

Targeting propagation via attribution

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A, attributed to an Entity C, targets an Entity BThe Entity C targets the Entity B
+

Targeting_via_attribution

+

Targeting propagation via belonging

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A targets an Identity B, part of an Identity CThe Entity A targets the Identity C
+

Targeting_via_belonging

+

Targeting propagation via location

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity targets a Location B and this Location B is located at a Location CThe Entity targets the Location C
+

Targeting_via_location

+

Targeting propagation when located

+ + + + + + + + + + + + + +
ConditionsCreations
An Entity A targets an Entity B and this target is located at Location D.The Entity A targets the Location D
+

Targeting_when_located

+

Rule execution

+

Rule activation

+

When a rule is activated, a background task is initiated. This task scans all platform data, identifying existing relationships that meet the conditions defined by the rule. Subsequently, it creates new objects (entities and/or relationships), expanding the network of insights within your threat intelligence environment. Then, activated rules operate continuously. Whenever a relationship is created or modified, and this change aligns with the conditions specified in an active rule, the reasoning mechanism is triggered. This ensures real-time relationship inference.

+

Rule deactivation

+

Deactivating a rule leads to the deletion of all objects and relationships created by it. This cleanup process maintains the accuracy and reliability of your threat intelligence database.

+

Access restrictions and data impact

+

Access to the rule engine panel is restricted to administrators only. Regular users do not have visibility into this section of the platform. Administrators possess the authority to activate or deactivate rules.

+

The rules engine empowers OpenCTI with the capability to automatically establish intricate relationships within your data. However, these rules can lead to a very large number of objects created. Even if the operation is reversible, an administrator should consider the impact of activating a rule.

+

Additional resources

+
    +
  • Usefulness: To understand the benefits and results of these rules, refer to the Inferences and reasoning page in the User Guide section of the documentation.
  • +
  • New inference rule: Given the potential impact of a rule on your data, users are not allowed to add new rules. However, users can submit rule suggestions via a GitHub issue for evaluation. These suggestions are carefully evaluated by our team, ensuring continuous improvement and innovation.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/retentions/index.html b/6.2.X/administration/retentions/index.html new file mode 100755 index 00000000..cf25be67 --- /dev/null +++ b/6.2.X/administration/retentions/index.html @@ -0,0 +1,4996 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retention policies - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Retention policies

+

Retention rules serve the purpose of establishing data retention times, specifying when data should be automatically deleted from the platform. Users can define filters to target specific objects. Any object meeting these criteria that haven't been updated within the designated time frame will be permanently deleted.

+

Note that the data deleted by an active retention policy will not appear in the trash and thus cannot be restored.

+

Configuration

+

Retention rules can be configured in the "Settings > Customization > Retention policies" window. A set of parameters must be configured:

+
    +
  • Maximum retention days: Set the maximum number of days an object can remain unchanged before being eligible for deletion.
  • +
  • Filters: Define filters based on specific criteria to select the types of objects subject to retention rules.
  • +
+

An object will be removed if it meets the specified filters and hasn't been updated for the duration set in the "Maximum retention days" field.

+

Retention policy parameters

+

Verification process

+

Before activating a retention rule, users have the option to verify its impact using the "Verify" button. This action provides insight into the number of objects that currently match the rule's criteria and would be deleted if the rule is activated.

+

Items to  be deleted

+
+

Verify before activation

+

Always use the "Verify" feature to assess the potential impact of a retention rule before activating it. Once the rule is activated, data deletion will begin, and retrieval of the deleted data will not be possible.

+
+

Retention rules contribute to maintaining a streamlined and efficient data lifecycle within OpenCTI, ensuring that outdated or irrelevant information is systematically removed from the platform, thereby optimizing disk space usage.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/segregation/index.html b/6.2.X/administration/segregation/index.html new file mode 100755 index 00000000..e584d0c2 --- /dev/null +++ b/6.2.X/administration/segregation/index.html @@ -0,0 +1,5272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Marking restriction - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Marking restriction

+

Data segregation in the context of Cyber Threat Intelligence refers to the practice of categorizing and separating different types of data or information related to cybersecurity threats based on specific criteria.

+

This separation helps organizations manage and analyze threat intelligence more effectively and securely and the goal of data segregation is to ensure that only those individuals who are authorized to view a particular set of data have access to that set of data.

+

Practically, "Need-to-know basis" and "classification level" are data segregation measures.

+

Description

+

Marking definitions are essential in the context of data segregation to ensure that data is appropriately categorized and protected based on its sensitivity or classification level. Marking definitions establish a standardized framework for classifying data.

+

Marking Definition objects are unique among STIX objects in the STIX 2.1 standard in that they cannot be versioned. This restriction is in place to prevent the possibility of indirect alterations to the markings associated with a STIX Object.

+

Multiple markings can be added to the same object. Certain categories of marking definitions or trust groups may enforce rules that specify which markings take precedence over others or how some markings can be added to complement existing ones.

+

In OpenCTI, data is segregated based on knowledge marking. The diagram provided below illustrates the manner in which OpenCTI establishes connections between pieces of information to authorize data access for a user:

+

Data access diagram

+

Manage markings

+

Create new markings

+

To create a marking, you must first possess the capability Manage marking definitions. For further information on user administration, please refer to the Users and Role Based Access Control page.

+

Once you have access to the settings, navigate to "Settings > Security > Marking Definitions" to create a new marking.

+

A marking consists of the following attributes:

+
    +
  • Type: Specifies the marking group to which it belongs.
  • +
  • Definition: The name assigned to the marking.
  • +
  • Color: The color associated with the marking.
  • +
  • Order: Determines the hierarchical order among markings of the same type.
  • +
+

Create a marking animation

+

Allowed marking

+

The configuration of authorized markings for a user is determined at the Group level. To access entities and relationships associated with specific markings, the user must belong to a group that has been granted access to those markings.

+

There are two ways in which markings can be accessed:

+
    +
  • The user is a member of a group that has been granted access to the marking.
  • +
  • The user is a member of a group that has access to a marking of the same type, with an equal or higher hierarchical order.
  • +
+
+

Access to an object with several markings

+

Access to all markings attached to an object is required in order to access it (not only one).

+
+
+

Automatically grant access to the new marking

+

To allow a group to automatically access a newly created marking definition, you can check Automatically authorize this group to new marking definition.

+
+

Default marking definitions

+

To apply a default marking when creating a new entity or relationship, you can choose which marking to add by default from the list of allowed markings. You can add only one marking per type, but you can have multiple types. This configuration is also done at the Group level.

+

Add default marking

+
+

Need a configuration change

+

Simply adding markings as default markings is insufficient to display the markings when creating an entity or relationship. You also need to enable default markings in the customization settings of an entity or relationship. For example, to enable default markings for a new report, navigate to "Settings > Customization > Report > Markings" and toggle the option to Activate/Desactivate default values.

+
+

Maximum shareable marking definitions

+

This configuration allows to define, for each type of marking definitions, until which level +we allow to share data externally (via Public dashboard or file export).

+

The marking definitions that can be shared by a group are the ones

+
    +
  • that are allowed for this group
  • +
  • and whose order are inferior or equal to the order of the maximum shareable markings defined for each marking type.
  • +
+

Users with the Bypass capability can share all the markings.

+

By default, every marking of a given marking type is shareable.

+

For example in the capture below, for the type of marking TLP, only data with a marking +definition that is allowed and has a level equal or below GREEN will be shareable. And no data with marking +definition statement will be shared at all.

+

Maximum marking definitions shareable

+

Management of multiple markings

+

In scenarios where multiple markings of the same type but different orders are added, the platform will retain only the marking with the highest order for each type. This consolidation can occurs in various instances:

+
    +
  • During entity creation, if multiple markings are selected.
  • +
  • During entity updates, whether manually or via a connector, if additional markings are introduced.
  • +
  • When multiple entities are merged, their respective markings will be amalgamated.
  • +
+

For example:

+

Create a new report and add markings PAP:AMBER,PAP:RED,TLP:AMBER+STRICT,TLP:CLEAR and a statement CC-BY-SA-4.0 DISARM Foundation

+

Create entity with markings

+

The final markings kept are: PAP:RED, TLP:AMBER+STRICT and CC-BY-SA-4.0 DISARM Foundation

+

Create entity with markings

+

Update an object manually

+

When update an entity or a relationship:

+
    +
  • add a marking with the same type and different orders, a pop-up will be displayed to confirm the choice,
  • +
  • add a marking with the same type and the same order, the marking will be added,
  • +
  • add a marking with different types, the marking will be added.
  • +
+

Update entity with markings, popup dialog

+

Import data from a connector

+

As a result of this mechanism, when importing data from a connector, the connector is unable to downgrade a marking for an entity if a marking of the same type is already present on it.

+

Additional information

+

The Traffic Light Protocol is implemented by default as marking definitions in OpenCTI. It allows you to segregate information by TLP levels in your platform and restrict access to marked data if users are not authorized to see the corresponding marking.

+

The Traffic Light Protocol (TLP) was designed by the Forum of Incident Response and Security Teams (FIRST) to provide a standardized method for classifying and handling sensitive information, based on four categories of sensitivity.

+

For more details, the diagram provided below illustrates how are categorized the marking definitions:

+

TLP diagram

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/support-package/index.html b/6.2.X/administration/support-package/index.html new file mode 100755 index 00000000..d248eb3e --- /dev/null +++ b/6.2.X/administration/support-package/index.html @@ -0,0 +1,4997 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Support package - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Support Package

+

Support packages are useful for troubleshooting issue that occurs on OpenCTI platform. +Administrators can request to create and download a support package that contains recent platform error logs and usage statistics.

+
+

Support package content

+

Even if we do our best to prevent logging any data, the support package may contains some sensitive information that you may not want to share with everyone. +Before creating a ticket with your support package takes some time to check if you can safely share the content depending of your security policy.

+
+

Support Package can be requested from "Settings > Support" menu.

+

Support package overview

+

Package generation

+

On a click on "Generate support package", a support event is propagated to every platform instances to request needed information. +Every instance that will receive this message will process the request and send the files to the platform. +During this processing the interface will display the expected support package name in an IN PROGRESS state waiting for completion. +After finishing the process the support package will move to the READY state and the buttons download and delete will be activated.

+

Package download

+

After file generation, using the download button will dynamically create a (zip) containing all instances logs and telemetry.

+

Partial package

+

In case of platform instability, some logs might not be retrieved and the support package will be incomplete.

+

If some instances fail to send their data, you will be able to force download a partial zip only after 1 minute. In case of a support package taking more than 5 minutes, the status will be moved to "timeout".

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/administration/users/index.html b/6.2.X/administration/users/index.html new file mode 100755 index 00000000..db8dc47c --- /dev/null +++ b/6.2.X/administration/users/index.html @@ -0,0 +1,5445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Users and RBAC - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+ +
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Users and Role Based Access Control

+

Introduction

+

In OpenCTI, the RBAC system not only related to what users can do or cannot do in the platform (aka. Capabilities) but also to the system of data segregation. Also, platform behavior such as default home dashboards, default triggers and digests as well as default hidden menus or entities can be defined across groups and organizations.

+

High level design

+

RBAC

+

Roles

+

Roles are used in the platform to grant the given groups with some capabilities to define what users in those groups can do or cannot do.

+

List of capabilities

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CapabilityDescription
Bypass all capabilitiesJust bypass everything including data segregation and enforcements.
Access knowledgeAccess in read-only to all the knowledge in the platform.
  Access to collaborative creationCreate notes and opinions (and modify its own) on entities and relations.
  Create / Update knowledgeCreate and update existing entities and relationships.
    Restrict organization accessShare entities and relationships with other organizations.
    Delete knowledgeDelete entities and relationships.
  Upload knowledge filesUpload files in the Data and Content section of entities.
  Download knowledge exportDownload the exports generated in the entities (in the Data section).
  Ask for knowledge enrichmentTrigger an enrichment for a given entity.
Access Dashboards and investigationsAccess to existing custom dashboards and investigations.
  Create / Update Dashboards and investigationsCreate and update custom dashboards and investigations.
    Delete Dashboards and investigationsDelete existing custom dashboards and investigations.
    Manage Public DashboardsManage public dashboards
Access connectorsRead information in the Data > Connectors section.
  Manage connector stateReset the connector state to restart ingestion from the beginning.
Access data sharing & ingestionAccess and consume data such as TAXII collections.
  Manage data sharing & ingestionShare data such as TAXII collections or custom dashboards.
  Manage CSV mappersCreate, update and delete CSV mappers.
Access administrationAccess and manage overall parameters of the platform in Settings > Parameters.
  Manage credentialsAccess and manage roles, groups, users, organizations and security policies.
  Manage marking definitionsUpdate and delete marking definitions.
  Manage labels & AttributesUpdate and delete labels, custom taxonomies, workflow and case templates.
Connectors API usage: register, ping, export push ...Connectors specific permissions for register, ping, push export files, etc.
Connect and consume the platform streams (/stream, /stream/live)List and consume the OpenCTI live streams.
Bypass mandatory references if anyIf external references enforced in a type of entity, be able to bypass the enforcement.
+

Manage roles

+

You can manage the roles in Settings > Security > Roles.

+

To create a role, just click on the + button:

+

Create role

+

Then you will be able to define the capabilities of the role:

+

Update role

+

Users

+

You can manage the users in Settings > Security > Users. If you are using Single-Sign-On (SSO), the users in OpenCTI are automatically created upon login.

+

To create a user, just click on the + button:

+

Create user

+

Manage a user

+

When access to a user, it is possible to:

+
    +
  • Visualize information including the token
  • +
  • Modify it, reset 2FA if necessary
  • +
  • Manage its sessions
  • +
  • Manage its triggers and digests
  • +
  • Visualize the history and operations
  • +
  • Manage its max confidence levels
  • +
+

User overview

+

From this view you can edit the user's information by clicking the "Update" button, which opens a panel with several tabs.

+
    +
  • Overview tab: edit all basic information such as the name or language
  • +
  • Password tab: change the password for this user
  • +
  • Groups tab: select the groups this user belongs to
  • +
  • Organization Admin tab: see Organization administration
  • +
  • Confidences tab: manage the user's maximum confidence level and overrides per entity type
  • +
+

manage user

+
+

Mandatory max confidence level

+

A user without Max confidence level won't have the ability to create, delete or update any data in our platform. Please be sure that your users are always either assigned to group that have a confidence level defined or that have an override of this group confidence level.

+
+

+

Groups

+

Groups are the main way to manage permissions and data segregation as well as platform customization for the given users part of this group. You can manage the groups in Settings > Security > Groups.

+

Here is the description of the group available parameters.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescription
Auto new markingsIf a new marking definition is created, this group will automatically be granted to it.
Default membershipIf a new user is created (manually or upon SSO), it will be added to this group.
RolesRoles and capabilities granted to the users belonging to this group.
Default dashboardCustomize the home dashboard for the users belonging to this group.
Default markingsIn Settings > Customization > Entity types, if a default marking definition is enabled, default markings of the group is used.
Allowed markingsGrant access to the group to the defined marking definitions, more details in data segregation.
Max shareable markingsGrant authorization to the group to share marking definitions.
Triggers and digestsDefine defaults triggers and digests for the users belonging to this group.
Max confidence levelDefine the maximum confidence level for the group: it will impact the capacity to update entities, the confidence level of a newly created entity by a user of the group.
+

Group overview

+
+

Max confidence level when a user has multiple groups

+

A user with multiple groups will have the the highest confidence level of all its groups. +For instance, if a user is part of group A (max confidence level = 100) and group B (max confidence level = 50), then the user max confidence level will be 100.

+
+

Manage a group

+

When managing a group, you can define the members and all above configurations.

+

Update a group

+

+

Organizations

+

Users can belong to organizations, which is an additional layer of data segregation and customization. To find out more about this part, please refer to the page on organization segregation.

+

Organization administration

+

Platform administrators can promote members of an organization as "Organization administrator". This elevated role grants them the necessary capabilities to create, edit and delete users from the corresponding Organization. Additionally, administrators have the flexibility to define a list of groups that can be granted to newly created members by the organization administrators. This feature simplifies the process of granting appropriate access and privileges to individuals joining the organization.

+

Organization admin Settings view

+

The platform administrator can promote/demote an organization admin through its user edition form.

+

Organization admin promoting/demoting

+
+

Organization admin rights

+

The "Organization admin" has restricted access to Settings. They can only manage the members of the organizations for which they have been promoted as "admins".

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/assets/css/custom.css b/6.2.X/assets/css/custom.css new file mode 100755 index 00000000..39e1f487 --- /dev/null +++ b/6.2.X/assets/css/custom.css @@ -0,0 +1,136 @@ +/* Color scheme */ + +[data-md-color-scheme="filigran"] { + /* Primary color shades */ + --md-primary-fg-color: #; + --md-primary-fg-color--light: #; + --md-primary-fg-color--dark: #; + --md-primary-bg-color: #; + --md-primary-bg-color--light: #; + + /* Accent color shades */ + --md-accent-fg-color: #1bb6ff; + --md-accent-fg-color--transparent: #; + --md-accent-bg-color: #; + --md-accent-bg-color--light: #; + + /* Default color shades */ + --md-default-fg-color: #00b1ff; + --md-default-fg-color--light: #697a94; + --md-default-fg-color--lighter: #3d4e66; + --md-default-fg-color--lightest: #03305d; + --md-default-bg-color: #070d19; + --md-default-bg-color--light: #0f1e38; + --md-default-bg-color--lighter: #152849; + --md-default-bg-color--lightest: #09101e; + + /* Code color shades */ + --md-code-fg-color: #ffffff; + --md-code-fg-color--light: #1bb6ff; + --md-code-bg-color: #001e3c; + --md-code-bg-color--light: #001e3c; + --md-code-bg-color--lighter: #001e3c; + + /* Code highlighting color shades */ + --md-code-hl-operator-color: #9ccc65; + --md-code-hl-punctuation-color: #cddc39; + --md-code-hl-comment-color: #3d4e66; + --md-code-hl-variable-color: #9ccc65; + --md-code-hl-name-color: #ffb300; + + /* Typeset color shades */ + --md-typeset-color: #ffffff; + + /* Typeset `a` color shades */ + --md-typeset-a-color: #0fbcff; + + /* Typeset `mark` color shades */ + --md-typeset-mark-color: #; + + /* Typeset `del` and `ins` color shades */ + --md-typeset-del-color: #; + --md-typeset-ins-color: #; + + /* Typeset `kbd` color shades */ + --md-typeset-kbd-color: #; + --md-typeset-kbd-accent-color: #; + --md-typeset-kbd-border-color: #; + + /* Typeset `table` color shades */ + --md-typeset-table-color: #0082d1; + --md-typeset-table-color--light: #051629; + + /* Admonition color shades */ + --md-admonition-fg-color: #ffffff; + --md-admonition-bg-color: var(--md-default-bg-color); + + /* Warning color shades */ + --md-warning-fg-color: #; + --md-warning-bg-color: #; + + /* Footer color shades */ + --md-footer-fg-color: #; + --md-footer-fg-color--light: #; + --md-footer-fg-color--lighter: #; + --md-footer-bg-color: #011222; + --md-footer-bg-color--dark: #09101e; +} + +/* Header */ +.md-header, .md-tabs { + background-color: #09101e !important; +} + +/* Cards */ +.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card { + border: none !important; + background-color: #001e3c !important; + border-radius: 10px; +} + +.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover { + border: none !important; + background-color: #001e3c !important; + border-radius: 10px; +} + +html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover { + color: #0fbcff !important; +} + +.middle { + margin: -5px 5px 0 0 !important; +} + +.md-header__button.md-logo img,.md-header__button.md-logo svg { + height: 1.5rem; +} + +.grid { + column-gap: 0.8rem !important; +} + +.md-typeset__table { + min-width: 100%; +} + +.md-typeset table:not([class]) { + display: table; +} + +.md-typeset__table td, th { + white-space: nowrap; +} + + +/* Works on Chrome, Edge, and Safari */ +html, body { + scrollbar-color: #070d19 #0f1e38; + scrollbarWidth: thin; + WebkitFontSmoothing: auto; +} + +.glightbox img { + border: 1px solid #273d5f; + border-radius: 4.8px; +} \ No newline at end of file diff --git a/6.2.X/assets/images/favicon.png b/6.2.X/assets/images/favicon.png new file mode 100755 index 00000000..17010cb5 Binary files /dev/null and b/6.2.X/assets/images/favicon.png differ diff --git a/6.2.X/assets/javascripts/bundle.13690f9d.min.js b/6.2.X/assets/javascripts/bundle.13690f9d.min.js new file mode 100755 index 00000000..3221c5ef --- /dev/null +++ b/6.2.X/assets/javascripts/bundle.13690f9d.min.js @@ -0,0 +1,3 @@ +"use strict";(()=>{var Bi=Object.create;var _r=Object.defineProperty;var Gi=Object.getOwnPropertyDescriptor;var Ji=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,Xi=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,uo=Object.prototype.propertyIsEnumerable;var fo=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,j=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&fo(e,r,t[r]);if(Bt)for(var r of Bt(t))uo.call(t,r)&&fo(e,r,t[r]);return e};var ho=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&uo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Zi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ji(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=Gi(t,n))||o.enumerable});return e};var Gt=(e,t,r)=>(r=e!=null?Bi(Xi(e)):{},Zi(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var bo=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var go=Cr((Hr,vo)=>{(function(e,t){typeof Hr=="object"&&typeof vo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Hr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(H){return!!(H&&H!==document&&H.nodeName!=="HTML"&&H.nodeName!=="BODY"&&"classList"in H&&"contains"in H.classList)}function c(H){var ft=H.type,Fe=H.tagName;return!!(Fe==="INPUT"&&s[ft]&&!H.readOnly||Fe==="TEXTAREA"&&!H.readOnly||H.isContentEditable)}function p(H){H.classList.contains("focus-visible")||(H.classList.add("focus-visible"),H.setAttribute("data-focus-visible-added",""))}function l(H){H.hasAttribute("data-focus-visible-added")&&(H.classList.remove("focus-visible"),H.removeAttribute("data-focus-visible-added"))}function f(H){H.metaKey||H.altKey||H.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(H){o=!1}function d(H){a(H.target)&&(o||c(H.target))&&p(H.target)}function g(H){a(H.target)&&(H.target.classList.contains("focus-visible")||H.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(H.target))}function L(H){document.visibilityState==="hidden"&&(n&&(o=!0),ee())}function ee(){document.addEventListener("mousemove",Z),document.addEventListener("mousedown",Z),document.addEventListener("mouseup",Z),document.addEventListener("pointermove",Z),document.addEventListener("pointerdown",Z),document.addEventListener("pointerup",Z),document.addEventListener("touchmove",Z),document.addEventListener("touchstart",Z),document.addEventListener("touchend",Z)}function ne(){document.removeEventListener("mousemove",Z),document.removeEventListener("mousedown",Z),document.removeEventListener("mouseup",Z),document.removeEventListener("pointermove",Z),document.removeEventListener("pointerdown",Z),document.removeEventListener("pointerup",Z),document.removeEventListener("touchmove",Z),document.removeEventListener("touchstart",Z),document.removeEventListener("touchend",Z)}function Z(H){H.target.nodeName&&H.target.nodeName.toLowerCase()==="html"||(o=!1,ne())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",L,!0),ee(),r.addEventListener("focus",d,!0),r.addEventListener("blur",g,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var io=Cr((Vt,no)=>{(function(t,r){typeof Vt=="object"&&typeof no=="object"?no.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Vt=="object"?Vt.ClipboardJS=r():t.ClipboardJS=r()})(Vt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Yi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(z){try{return document.execCommand(z)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},g=d;function L(z){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=z,_}var ee=function(C,_){var D=L(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},ne=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=ee(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=ee(C.value,_):(D=f()(C),u("copy")),D},Z=ne;function H(z){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?H=function(_){return typeof _}:H=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},H(z)}var ft=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,Ue=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&H(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ue)return Z(Ue,{container:N});if(G)return D==="cut"?g(G):Z(G,{container:N})},Fe=ft;function R(z){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(z)}function se(z,C){if(!(z instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(z,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(Ue){return G.onClick(Ue)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,Ue=this.action(G)||"copy",Yt=Fe({action:Ue,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:Ue,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return Z(N,G)}},{key:"cut",value:function(N){return g(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,Ue=!!document.queryCommandSupported;return G.forEach(function(Yt){Ue=Ue&&!!document.queryCommandSupported(Yt)}),Ue}}]),_}(a()),Yi=Qi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,d,g){var L=p.apply(this,arguments);return l.addEventListener(u,L,g),{destroy:function(){l.removeEventListener(u,L,g)}}}function c(l,f,u,d,g){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(L){return a(L,f,u,d,g)}))}function p(l,f,u,d){return function(g){g.delegateTarget=s(g.target,f),g.delegateTarget&&d.call(l,g)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,d,g){if(!u&&!d&&!g)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(g))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,g);if(s.nodeList(u))return l(u,d,g);if(s.string(u))return f(u,d,g);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,g){return u.addEventListener(d,g),{destroy:function(){u.removeEventListener(d,g)}}}function l(u,d,g){return Array.prototype.forEach.call(u,function(L){L.addEventListener(d,g)}),{destroy:function(){Array.prototype.forEach.call(u,function(L){L.removeEventListener(d,g)})}}}function f(u,d,g){return a(document.body,u,d,g)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";var fs=/["'&<>]/;di.exports=us;function us(e){var t=""+e,r=fs.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function q(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,d)})})}function a(u,d){try{c(o[u](d))}catch(g){f(i[0][3],g)}}function c(u){u.value instanceof ut?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,d){u(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function Eo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function P(e){return typeof e=="function"}function xt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Xt=xt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Xe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var ze=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(L){t={error:L}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(P(l))try{l()}catch(L){i=L instanceof Xt?L.errors:[L]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var g=d.value;try{wo(g)}catch(L){i=i!=null?i:[],L instanceof Xt?i=B(B([],q(i)),q(L.errors)):i.push(L)}}}catch(L){o={error:L}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Xt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)wo(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Xe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Xe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var $r=ze.EMPTY;function Zt(e){return e instanceof ze||e&&"closed"in e&&P(e.remove)&&P(e.add)&&P(e.unsubscribe)}function wo(e){P(e)?e():e.unsubscribe()}var We={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var yt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new ze(function(){o.currentObservers=null,Xe(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new I;return r.source=this,r},t.create=function(r,o){return new Co(r,o)},t}(I);var Co=function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t}(w);var jr=function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(w);var Pt={now:function(){return(Pt.delegate||Date).now()},delegate:void 0};var It=function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Pt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(St);var $o=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(Ot);var Dr=new $o(ko);var Ro=function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(St);var Po=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(Ot);var ge=new Po(Ro);var x=new I(function(e){return e.complete()});function rr(e){return e&&P(e.schedule)}function Nr(e){return e[e.length-1]}function ct(e){return P(Nr(e))?e.pop():void 0}function Ie(e){return rr(Nr(e))?e.pop():void 0}function or(e,t){return typeof Nr(e)=="number"?e.pop():t}var Lt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function nr(e){return P(e==null?void 0:e.then)}function ir(e){return P(e[wt])}function ar(e){return Symbol.asyncIterator&&P(e==null?void 0:e[Symbol.asyncIterator])}function sr(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function ca(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var cr=ca();function pr(e){return P(e==null?void 0:e[cr])}function lr(e){return yo(this,arguments,function(){var r,o,n,i;return Jt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,ut(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,ut(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,ut(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function mr(e){return P(e==null?void 0:e.getReader)}function U(e){if(e instanceof I)return e;if(e!=null){if(ir(e))return pa(e);if(Lt(e))return la(e);if(nr(e))return ma(e);if(ar(e))return Io(e);if(pr(e))return fa(e);if(mr(e))return ua(e)}throw sr(e)}function pa(e){return new I(function(t){var r=e[wt]();if(P(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function la(e){return new I(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?v(function(n,i){return e(n,i,o)}):be,Ee(1),r?rt(t):Zo(function(){return new ur}))}}function Yr(e){return e<=0?function(){return x}:y(function(t,r){var o=[];t.subscribe(E(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new w}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,g=!1,L=!1,ee=function(){f==null||f.unsubscribe(),f=void 0},ne=function(){ee(),l=u=void 0,g=L=!1},Z=function(){var H=l;ne(),H==null||H.unsubscribe()};return y(function(H,ft){d++,!L&&!g&&ee();var Fe=u=u!=null?u:r();ft.add(function(){d--,d===0&&!L&&!g&&(f=Br(Z,c))}),Fe.subscribe(ft),!l&&d>0&&(l=new ht({next:function(R){return Fe.next(R)},error:function(R){L=!0,ee(),f=Br(ne,n,R),Fe.error(R)},complete:function(){g=!0,ee(),f=Br(ne,s),Fe.complete()}}),U(H).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function F(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ve(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ha=O(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),K(void 0),m(()=>Ve()||document.body),X(1));function Ke(e){return Ha.pipe(m(t=>e.contains(t)),Y())}function nt(e,t){return k(()=>O(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>ke(+!r*t)):be,K(e.matches(":hover"))))}function nn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)nn(e,r)}function S(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)nn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function At(e){let t=S("script",{src:e});return k(()=>(document.head.appendChild(t),O(h(t,"load"),h(t,"error").pipe(b(()=>Vr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var an=new w,ka=k(()=>typeof ResizeObserver=="undefined"?At("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>an.next(t)))),b(e=>O(et,$(e)).pipe(A(()=>e.disconnect()))),X(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return ka.pipe(T(r=>r.observe(t)),b(r=>an.pipe(v(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),K(de(e)))}function Ct(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function sn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Qe(e){return{x:e.offsetLeft,y:e.offsetTop}}function cn(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function pn(e){return O(h(window,"load"),h(window,"resize")).pipe($e(0,ge),m(()=>Qe(e)),K(Qe(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ye(e){return O(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ge),m(()=>gr(e)),K(gr(e)))}var ln=new w,$a=k(()=>$(new IntersectionObserver(e=>{for(let t of e)ln.next(t)},{threshold:0}))).pipe(b(e=>O(et,$(e)).pipe(A(()=>e.disconnect()))),X(1));function lt(e){return $a.pipe(T(t=>t.observe(e)),b(t=>ln.pipe(v(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function mn(e,t=16){return Ye(e).pipe(m(({y:r})=>{let o=de(e),n=Ct(e);return r>=n.height-o.height-t}),Y())}var xr={drawer:F("[data-md-toggle=drawer]"),search:F("[data-md-toggle=search]")};function fn(e){return xr[e].checked}function it(e,t){xr[e].checked!==t&&xr[e].click()}function Be(e){let t=xr[e];return h(t,"change").pipe(m(()=>t.checked),K(t.checked))}function Ra(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Pa(){return O(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(K(!1))}function un(){let e=h(window,"keydown").pipe(v(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:fn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),v(({mode:t,type:r})=>{if(t==="global"){let o=Ve();if(typeof o!="undefined")return!Ra(o,r)}return!0}),le());return Pa().pipe(b(t=>t?x:e))}function we(){return new URL(location.href)}function at(e,t=!1){if(Q("navigation.instant")&&!t){let r=S("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function dn(){return new w}function hn(){return location.hash.slice(1)}function bn(e){let t=S("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return O(h(window,"hashchange"),e).pipe(m(hn),K(hn()),v(t=>t.length>0),X(1))}function vn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),v(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return dr(r=>t.addListener(()=>r(t.matches))).pipe(K(t.matches))}function gn(){let e=matchMedia("print");return O(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(K(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():x))}function to(e,t){return new I(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function Ge(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),X(1))}function yr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),X(1))}function xn(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),X(1))}function yn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function En(){return O(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(yn),K(yn()))}function wn(){return{width:innerWidth,height:innerHeight}}function Tn(){return h(window,"resize",{passive:!0}).pipe(m(wn),K(wn()))}function Sn(){return V([En(),Tn()]).pipe(m(([e,t])=>({offset:e,size:t})),X(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(oe("size")),n=V([o,r]).pipe(m(()=>Qe(e)));return V([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Ia(e){return h(e,"message",t=>t.data)}function Fa(e){let t=new w;return t.subscribe(r=>e.postMessage(r)),t}function On(e,t=new Worker(e)){let r=Ia(t),o=Fa(t),n=new w;n.subscribe(o);let i=o.pipe(re(),ae(!0));return n.pipe(re(),Ne(r.pipe(W(i))),le())}var ja=F("#__config"),Ht=JSON.parse(ja.textContent);Ht.base=`${new URL(Ht.base,we())}`;function Te(){return Ht}function Q(e){return Ht.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ht.translations[e].replace("#",t.toString()):Ht.translations[e]}function Ce(e,t=document){return F(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Ua(e){let t=F(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>F(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function Ln(e){if(!Q("announce.dismiss")||!e.childElementCount)return x;if(!e.hidden){let t=F(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return k(()=>{let t=new w;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Ua(e).pipe(T(r=>t.next(r)),A(()=>t.complete()),m(r=>j({ref:e},r)))})}function Wa(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function Mn(e,t){let r=new w;return r.subscribe(({hidden:o})=>{e.hidden=o}),Wa(e,t).pipe(T(o=>r.next(o)),A(()=>r.complete()),m(o=>j({ref:e},o)))}function Dt(e,t){return t==="inline"?S("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},S("div",{class:"md-tooltip__inner md-typeset"})):S("div",{class:"md-tooltip",id:e,role:"tooltip"},S("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return S("div",{class:"md-tooltip2",role:"dialog"},S("div",{class:"md-tooltip2__inner md-typeset"},e))}function _n(...e){return S("div",{class:"md-tooltip2",role:"tooltip"},S("div",{class:"md-tooltip2__inner md-typeset"},e))}function An(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return S("aside",{class:"md-annotation",tabIndex:0},Dt(t),S("a",{href:r,class:"md-annotation__index",tabIndex:-1},S("span",{"data-md-annotation-id":e})))}else return S("aside",{class:"md-annotation",tabIndex:0},Dt(t),S("span",{class:"md-annotation__index",tabIndex:-1},S("span",{"data-md-annotation-id":e})))}function Cn(e){return S("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function Hn(){return S("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function kn(){return S("nav",{class:"md-code__nav"})}function ro(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,S("del",null,p)," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);Q("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return S("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},S("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&S("div",{class:"md-search-result__icon md-icon"}),r>0&&S("h1",null,e.title),r<=0&&S("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return S("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&S("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function $n(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scorero(l,1)),...c.length?[S("details",{class:"md-search-result__more"},S("summary",{tabIndex:-1},S("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>ro(l,1)))]:[]];return S("li",{class:"md-search-result__item"},p)}function Rn(e){return S("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>S("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function oo(e){let t=`tabbed-control tabbed-control--${e}`;return S("div",{class:t,hidden:!0},S("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Pn(e){return S("div",{class:"md-typeset__scrollwrap"},S("div",{class:"md-typeset__table"},e))}function Da(e){let t=Te(),r=new URL(`../${e.version}/`,t.base);return S("li",{class:"md-version__item"},S("a",{href:`${r}`,class:"md-version__link"},e.title))}function In(e,t){return e=e.filter(r=>{var o;return!((o=r.properties)!=null&&o.hidden)}),S("div",{class:"md-version"},S("button",{class:"md-version__current","aria-label":Me("select.version")},t.title),S("ul",{class:"md-version__list"},e.map(Da)))}var Na=0;function Va(e,t=250){let r=V([Ke(e),nt(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=k(()=>sn(e)).pipe(J(Ye),gt(1),m(()=>cn(e)));return r.pipe(Re(n=>n),b(()=>V([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Nt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Na++}`;return k(()=>{let s=new w,a=new jr(!1);s.pipe(re(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>ke(+!l*250,Dr)),Y(),b(l=>l?o:x),T(l=>l.id=i),le());V([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>nt(l,250)),K(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(v(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),g=d.width/2;if(f.role==="tooltip")return{x:g,y:8+d.height};if(d.y>=u.height/2){let{height:L}=de(f);return{x:g,y:-16-L}}else return{x:g,y:16+d.height}}));return V([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(v(l=>l),te(c,(l,f)=>f),v(l=>l.role==="tooltip")).subscribe(l=>{let f=de(F(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ge),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),V([a.pipe(v(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(v(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Va(e,r).pipe(T(l=>s.next(l)),A(()=>s.complete()),m(l=>j({ref:e},l)))})}function Je(e,{viewport$:t},r=document.body){return Nt(e,{content$:new I(o=>{let n=e.title,i=_n(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function za(e,t){let r=k(()=>V([pn(e),Ye(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ke(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Fn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return k(()=>{let i=new w,s=i.pipe(re(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),lt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),O(i.pipe(v(({active:a})=>a)),i.pipe(Ae(250),v(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ge)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ge),v(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),v(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ve())==null||p.blur()}}),r.pipe(W(s),v(a=>a===o),ot(125)).subscribe(()=>e.focus()),za(e,t).pipe(T(a=>i.next(a)),A(()=>i.complete()),m(a=>j({ref:e},a)))})}function qa(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(typeof t.annotate!="undefined"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Ka(e){let t=[];for(let r of qa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function jn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Ka(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,An(c,i)),a.replaceWith(s.get(c)))}return s.size===0?x:k(()=>{let a=new w,c=a.pipe(re(),ae(!0)),p=[];for(let[l,f]of s)p.push([F(".md-typeset",f),F(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?jn(f,u):jn(u,f)}),O(...[...s].map(([,l])=>Fn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Un(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Un(t)}}function Wn(e,t){return k(()=>{let r=Un(e);return typeof r!="undefined"?Tr(r,e,t):x})}var Nn=Gt(io());var Qa=0,Dn=O(h(window,"keydown").pipe(m(()=>!0)),O(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(K(!1),X(1));function Vn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Vn(t)}}function Ya(e){return Le(e).pipe(m(({width:t})=>({scrollable:Ct(e).width>t})),oe("scrollable"))}function zn(e,t){let{matches:r}=matchMedia("(hover)"),o=k(()=>{let n=new w,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Qa++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Vn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||Q("content.code.annotate"))){let g=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:L,height:ee})=>L&&ee),Y(),b(L=>L?g:x)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||Q("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),g=Hn();s.push(g),Q("content.tooltips")&&l.push(Je(g,{viewport$}));let L=h(g,"click").pipe(Ut(R=>!R,!1),T(()=>g.blur()),le());L.subscribe(R=>{g.classList.toggle("md-code__button--active",R)});let ee=fe(u).pipe(J(R=>nt(R).pipe(m(se=>[R,se]))));L.pipe(b(R=>R?ee:x)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let ne=fe(u).pipe(J(R=>h(R,"mousedown").pipe(T(se=>se.preventDefault()),m(()=>R)))),Z=L.pipe(b(R=>R?ne:x),te(Dn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(je=>u.indexOf(je.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),H=Zr(x).pipe(v(R=>R.startsWith(`__codelineno-${p}-`)));H.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let je=document.createElement("span");je.className="hll",je.append(...Array.from(Se.childNodes).slice(1)),Se.append(je)}}),H.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let Fe=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(T(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(Dn),m(([R,se])=>{let he=+F(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(je=>+je.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));O(Z,Fe).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Nn.default.isSupported()&&(e.closest(".copy")||Q("content.code.copy")&&!e.closest(".no-copy"))){let d=Cn(a.id);s.push(d),Q("content.tooltips")&&l.push(Je(d,{viewport$}))}if(s.length){let d=kn();d.append(...s),a.insertBefore(d,e)}return Ya(e).pipe(T(d=>n.next(d)),A(()=>n.complete()),m(d=>j({ref:e},d)),Ne(O(...l).pipe(W(i))))});return Q("content.lazy")?lt(e).pipe(v(n=>n),Ee(1),b(()=>o)):o}function Ba(e,{target$:t,print$:r}){let o=!0;return O(t.pipe(m(n=>n.closest("details:not([open])")),v(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(v(n=>n||!o),T(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function qn(e,t){return k(()=>{let r=new w;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Ba(e,t).pipe(T(o=>r.next(o)),A(()=>r.complete()),m(o=>j({ref:e},o)))})}function Ga(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function Ja(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}return $(e)}function Kn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return x;if(!(Q("navigation.instant.preview")||e.hasAttribute("data-preview")))return x;let o=V([Ke(e),nt(e)]).pipe(m(([i,s])=>i||s),Y(),v(i=>i));return bt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):x}),b(i=>yr(i).pipe(b(s=>Ja(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?x:$(Ga(a))})).pipe(b(i=>{let s=new I(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Nt(e,j({content$:s},t))}))}var Qn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var ao,Za=0;function es(){return typeof mermaid=="undefined"||mermaid instanceof Element?At("https://unpkg.com/mermaid@10/dist/mermaid.min.js"):$(void 0)}function Yn(e){return e.classList.remove("mermaid"),ao||(ao=es().pipe(T(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Qn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),X(1))),ao.subscribe(()=>bo(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Za++}`,r=S("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),ao.pipe(m(()=>({ref:e})))}var Bn=S("table");function Gn(e){return e.replaceWith(Bn),Bn.replaceWith(Pn(e)),$({ref:e})}function ts(e){let t=e.find(r=>r.checked)||e[0];return O(...e.map(r=>h(r,"change").pipe(m(()=>F(`label[for="${r.id}"]`))))).pipe(K(F(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Jn(e,{viewport$:t,target$:r}){let o=F(".tabbed-labels",e),n=M(":scope > input",e),i=oo("prev");e.append(i);let s=oo("next");return e.append(s),k(()=>{let a=new w,c=a.pipe(re(),ae(!0));V([a,Le(e)]).pipe(W(c),$e(1,ge)).subscribe({next([{active:p},l]){let f=Qe(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),V([Ye(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=Ct(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),O(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),v(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=F(`label[for="${p.id}"]`);l.replaceChildren(S("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),v(f=>!(f.metaKey||f.ctrlKey)),T(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return Q("content.tabs.link")&&a.pipe(Pe(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let g of M("[data-tabs]"))for(let L of M(":scope > input",g)){let ee=F(`label[for="${L.id}"]`);if(ee!==p&&ee.innerText.trim()===f){ee.setAttribute("data-md-switching",""),L.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.pause()}),lt(e).pipe(b(()=>ts(n)),T(p=>a.next(p)),A(()=>a.complete()),m(p=>j({ref:e},p)))}).pipe(Ze(pe))}function Xn(e,t){let{viewport$:r,target$:o,print$:n}=t;return O(...M(".annotate:not(.highlight)",e).map(i=>Wn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>zn(i,{target$:o,print$:n})),...M("a:not([title])",e).map(i=>Kn(i,t)),...M("pre.mermaid",e).map(i=>Yn(i)),...M("table:not([class])",e).map(i=>Gn(i)),...M("details",e).map(i=>qn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>Jn(i,{viewport$:r,target$:o})),...M("[title]",e).filter(()=>Q("content.tooltips")).map(i=>Je(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>Q("content.footnote.tooltips")).map(i=>Nt(i,{content$:new I(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function rs(e,{alert$:t}){return t.pipe(b(r=>O($(!0),$(!1).pipe(ot(2e3))).pipe(m(o=>({message:r,active:o})))))}function Zn(e,t){let r=F(".md-typeset",e);return k(()=>{let o=new w;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),rs(e,t).pipe(T(n=>o.next(n)),A(()=>o.complete()),m(n=>j({ref:e},n)))})}var os=0;function ns(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ye(o):$({x:0,y:0}),i=O(Ke(t),nt(t)).pipe(Y());return V([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Qe(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ei(e){let t=e.title;if(!t.length)return x;let r=`__tooltip_${os++}`,o=Dt(r,"inline"),n=F(".md-typeset",o);return n.innerHTML=t,k(()=>{let i=new w;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),O(i.pipe(v(({active:s})=>s)),i.pipe(Ae(250),v(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ge)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ge),v(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ns(o,e).pipe(T(s=>i.next(s)),A(()=>i.complete()),m(s=>j({ref:e},s)))}).pipe(Ze(pe))}function is({viewport$:e}){if(!Q("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),tt(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Be("search");return V([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),K(!1))}function ti(e,t){return k(()=>V([Le(e),is(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),X(1))}function ri(e,{header$:t,main$:r}){return k(()=>{let o=new w,n=o.pipe(re(),ae(!0));o.pipe(oe("active"),De(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(v(()=>Q("content.tooltips")),J(s=>ei(s)));return r.subscribe(o),t.pipe(W(n),m(s=>j({ref:e},s)),Ne(i.pipe(W(n))))})}function as(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:o>=n}}),oe("active"))}function oi(e,t){return k(()=>{let r=new w;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?x:as(o,t).pipe(T(n=>r.next(n)),A(()=>r.complete()),m(n=>j({ref:e},n)))})}function ni(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),oe("bottom"))));return V([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function ss(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),K(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),X(1))}function ii(e){let t=M("input",e),r=S("meta",{name:"theme-color"});document.head.appendChild(r);let o=S("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return k(()=>{let i=new w;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),ss(t).pipe(W(n.pipe(Pe(1))),vt(),T(s=>i.next(s)),A(()=>i.complete()),m(s=>j({ref:e},s)))})}function ai(e,{progress$:t}){return k(()=>{let r=new w;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(T(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function si(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function cs(e,t){let r=new Map;for(let o of M("url",e)){let n=F("loc",o),i=[si(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(si(new URL(a),t))}}return r}function kt(e){return xn(new URL("sitemap.xml",e)).pipe(m(t=>cs(t,new URL(e))),ye(()=>$(new Map)),le())}function ci({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),v(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ye(()=>x)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(v(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return x;let[i,s]=n,a=we();if(a.href.startsWith(i))return x;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return x})).subscribe(r=>at(r,!0))}var so=Gt(io());function ps(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function pi({alert$:e}){so.default.isSupported()&&new I(t=>{new so.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ps(F(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(T(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function li(e,t){if(!(e.target instanceof Element))return x;let r=e.target.closest("a");if(r===null)return x;if(r.target||e.metaKey||e.ctrlKey)return x;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):x}function mi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function fi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function ls(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...Q("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=mi(document);for(let[o,n]of mi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return qe(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new I(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),x}),re(),ae(document))}function ui({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return x;$(document).subscribe(fi);let n=h(document.body,"click").pipe(De(e),b(([a,c])=>li(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),O(n,i).subscribe(t);let s=t.pipe(oe("pathname"),b(a=>yr(a,{progress$:o}).pipe(ye(()=>(at(a,!0),x)))),b(fi),b(ls),le());return O(s.pipe(te(t,(a,c)=>c)),t.pipe(oe("pathname"),b(()=>t),oe("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),T(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",bn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(oe("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),Q("navigation.instant.prefetch")&&O(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(De(e),b(([a,c])=>li(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var bi=Gt(hi());function vi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,bi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function gi(e,t){let r=On(e);return O($(location.protocol!=="file:"),Be("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:Q("search.suggest")}}})),r}function xi({document$:e}){let t=Te(),r=Ge(new URL("../versions.json",t.base)).pipe(ye(()=>x)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(v(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?x:(i.preventDefault(),$(c))}}return x}),b(i=>kt(new URL(i)).pipe(m(s=>{let c=we().href.replace(t.base,i);return s.has(c.split("#")[0])?new URL(c):new URL(i)})))))).subscribe(n=>at(n,!0)),V([r,o]).subscribe(([n,i])=>{F(".md-header__topic").appendChild(In(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of me("outdated"))a.hidden=!1})}function hs(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(it("search",!0),e.value=r.get("q"),e.focus(),Be("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ke(e),n=O(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return V([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),X(1))}function yi(e,{worker$:t}){let r=new w,o=r.pipe(re(),ae(!0));V([t.pipe(Re(zt)),r],(i,s)=>s).pipe(oe("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(oe("focus")).subscribe(({focus:i})=>{i&&it("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=F("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),hs(e,{worker$:t}).pipe(T(i=>r.next(i)),A(()=>r.complete()),m(i=>j({ref:e},i)),X(1))}function Ei(e,{worker$:t,query$:r}){let o=new w,n=mn(e.parentElement).pipe(v(Boolean)),i=e.parentElement,s=F(":scope > :first-child",e),a=F(":scope > :last-child",e);Be("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(T(()=>a.innerHTML=""),b(({items:l})=>O($(...l.slice(0,10)),$(...l.slice(10)).pipe(tt(4),Xr(n),b(([f])=>f)))),m($n),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?x:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(v(Sr),m(({data:l})=>l)).pipe(T(l=>o.next(l)),A(()=>o.complete()),m(l=>j({ref:e},l)))}function bs(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function wi(e,t){let r=new w,o=r.pipe(re(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),bs(e,t).pipe(T(n=>r.next(n)),A(()=>r.complete()),m(n=>j({ref:e},n)))}function Ti(e,{worker$:t,keyboard$:r}){let o=new w,n=Ce("search-query"),i=O(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(De(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(v(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(v(Sr),m(({data:a})=>a)).pipe(T(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function Si(e,{index$:t,keyboard$:r}){let o=Te();try{let n=gi(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(v(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>it("search",!1)),r.pipe(v(({mode:c})=>c==="search")).subscribe(c=>{let p=Ve();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":it("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ve()&&i.focus()}}),r.pipe(v(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=yi(i,{worker$:n});return O(a,Ei(s,{worker$:n,query$:a})).pipe(Ne(...me("search-share",e).map(c=>wi(c,{query$:a})),...me("search-suggest",e).map(c=>Ti(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,et}}function Oi(e,{index$:t,location$:r}){return V([t,r.pipe(K(we()),v(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>vi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=S("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function vs(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return V([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function co(e,o){var n=o,{header$:t}=n,r=ho(n,["header$"]);let i=F(".md-sidebar__scrollwrap",e),{y:s}=Qe(i);return k(()=>{let a=new w,c=a.pipe(re(),ae(!0)),p=a.pipe($e(0,ge));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=F(`[id="${l.htmlFor}"]`);F(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Q("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Je(l,{viewport$})),W(c)).subscribe(),vs(e,r).pipe(T(l=>a.next(l)),A(()=>a.complete()),m(l=>j({ref:e},l)))})}function Li(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return bt(Ge(`${r}/releases/latest`).pipe(ye(()=>x),m(o=>({version:o.tag_name})),rt({})),Ge(r).pipe(ye(()=>x),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),rt({}))).pipe(m(([o,n])=>j(j({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ge(r).pipe(m(o=>({repositories:o.public_repos})),rt({}))}}function Mi(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ge(r).pipe(ye(()=>x),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),rt({}))}function _i(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Li(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return Mi(r,o)}return x}var gs;function xs(e){return gs||(gs=k(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return x}return _i(e.href).pipe(T(o=>__md_set("__source",o,sessionStorage)))}).pipe(ye(()=>x),v(t=>Object.keys(t).length>0),m(t=>({facts:t})),X(1)))}function Ai(e){let t=F(":scope > :last-child",e);return k(()=>{let r=new w;return r.subscribe(({facts:o})=>{t.appendChild(Rn(o)),t.classList.add("md-source__repository--active")}),xs(e).pipe(T(o=>r.next(o)),A(()=>r.complete()),m(o=>j({ref:e},o)))})}function ys(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),oe("hidden"))}function Ci(e,t){return k(()=>{let r=new w;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(Q("navigation.tabs.sticky")?$({hidden:!1}):ys(e,t)).pipe(T(o=>r.next(o)),A(()=>r.complete()),m(o=>j({ref:e},o)))})}function Es(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(oe("height"),m(({height:a})=>{let c=Ce("main"),p=F(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(oe("height"),b(a=>k(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),De(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let g=u+d.height>=Math.floor(a.height);for(;f.length;){let[,L]=f[0];if(L-p=u&&!g)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),K({prev:[],next:[]}),tt(2,1),m(([a,c])=>a.prev.length{let i=new w,s=i.pipe(re(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),Q("toc.follow")){let a=O(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(v(({prev:c})=>c.length>0),De(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return Q("navigation.tracking")&&t.pipe(W(s),oe("offset"),Ae(250),Pe(1),W(n.pipe(Pe(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Es(e,{viewport$:t,header$:r}).pipe(T(a=>i.next(a)),A(()=>i.complete()),m(a=>j({ref:e},a)))})}function ws(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),tt(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return V([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Pe(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function ki(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new w,s=i.pipe(re(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),oe("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),ws(e,{viewport$:t,main$:o,target$:n}).pipe(T(a=>i.next(a)),A(()=>i.complete()),m(a=>j({ref:e},a)))}function $i({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>lt(r).pipe(W(e.pipe(Pe(1))),v(o=>o),m(()=>r),Ee(1))),v(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,Je(n,{viewport$:t}).pipe(W(e.pipe(Pe(1))),A(()=>n.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>M(".md-status")),J(r=>Je(r,{viewport$:t}))).subscribe()}function Ri({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),T(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Ts(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Pi({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),T(t=>t.removeAttribute("data-md-scrollfix")),v(Ts),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Ii({viewport$:e,tablet$:t}){V([Be("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(ot(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function Ss(){return location.protocol==="file:"?At(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),X(1)):Ge(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var st=on(),Kt=dn(),$t=vn(Kt),po=un(),He=Sn(),Lr=Wt("(min-width: 960px)"),ji=Wt("(min-width: 1220px)"),Ui=gn(),Or=Te(),Wi=document.forms.namedItem("search")?Ss():et,lo=new w;pi({alert$:lo});ci({document$:st});var mo=new w,Di=kt(Or.base);Q("navigation.instant")&&ui({sitemap$:Di,location$:Kt,viewport$:He,progress$:mo}).subscribe(st);var Fi;((Fi=Or.version)==null?void 0:Fi.provider)==="mike"&&xi({document$:st});O(Kt,$t).pipe(ot(125)).subscribe(()=>{it("drawer",!1),it("search",!1)});po.pipe(v(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&at(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&at(r);break;case"Enter":let o=Ve();o instanceof HTMLLabelElement&&o.click()}});$i({viewport$:He,document$:st});Ri({document$:st,tablet$:Lr});Pi({document$:st});Ii({viewport$:He,tablet$:Lr});var mt=ti(Ce("header"),{viewport$:He}),qt=st.pipe(m(()=>Ce("main")),b(e=>ni(e,{viewport$:He,header$:mt})),X(1)),Os=O(...me("consent").map(e=>Mn(e,{target$:$t})),...me("dialog").map(e=>Zn(e,{alert$:lo})),...me("header").map(e=>ri(e,{viewport$:He,header$:mt,main$:qt})),...me("palette").map(e=>ii(e)),...me("progress").map(e=>ai(e,{progress$:mo})),...me("search").map(e=>Si(e,{index$:Wi,keyboard$:po})),...me("source").map(e=>Ai(e))),Ls=k(()=>O(...me("announce").map(e=>Ln(e)),...me("content").map(e=>Xn(e,{sitemap$:Di,viewport$:He,target$:$t,print$:Ui})),...me("content").map(e=>Q("search.highlight")?Oi(e,{index$:Wi,location$:Kt}):x),...me("header-title").map(e=>oi(e,{viewport$:He,header$:mt})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(ji,()=>co(e,{viewport$:He,header$:mt,main$:qt})):eo(Lr,()=>co(e,{viewport$:He,header$:mt,main$:qt}))),...me("tabs").map(e=>Ci(e,{viewport$:He,header$:mt})),...me("toc").map(e=>Hi(e,{viewport$:He,header$:mt,main$:qt,target$:$t})),...me("top").map(e=>ki(e,{viewport$:He,header$:mt,main$:qt,target$:$t})))),Ni=st.pipe(b(()=>Ls),Ne(Os),X(1));Ni.subscribe();window.document$=st;window.location$=Kt;window.target$=$t;window.keyboard$=po;window.viewport$=He;window.tablet$=Lr;window.screen$=ji;window.print$=Ui;window.alert$=lo;window.progress$=mo;window.component$=Ni;})(); diff --git a/6.2.X/assets/javascripts/glightbox.min.js b/6.2.X/assets/javascripts/glightbox.min.js new file mode 100755 index 00000000..614fb188 --- /dev/null +++ b/6.2.X/assets/javascripts/glightbox.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).GLightbox=t()}(this,(function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var i=0;i1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=e[s]=e[s]||[],l={all:n,evt:null,found:null};return t&&i&&P(n)>0&&o(n,(function(e,n){if(e.eventName==t&&e.fn.toString()==i.toString())return l.found=!0,l.evt=n,!1})),l}function a(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=t.onElement,n=t.withCallback,s=t.avoidDuplicate,l=void 0===s||s,a=t.once,h=void 0!==a&&a,d=t.useCapture,c=void 0!==d&&d,u=arguments.length>2?arguments[2]:void 0,g=i||[];function v(e){T(n)&&n.call(u,e,this),h&&v.destroy()}return C(g)&&(g=document.querySelectorAll(g)),v.destroy=function(){o(g,(function(t){var i=r(t,e,v);i.found&&i.all.splice(i.evt,1),t.removeEventListener&&t.removeEventListener(e,v,c)}))},o(g,(function(t){var i=r(t,e,v);(t.addEventListener&&l&&!i.found||!l)&&(t.addEventListener(e,v,c),i.all.push({eventName:e,fn:v}))})),v}function h(e,t){o(t.split(" "),(function(t){return e.classList.add(t)}))}function d(e,t){o(t.split(" "),(function(t){return e.classList.remove(t)}))}function c(e,t){return e.classList.contains(t)}function u(e,t){for(;e!==document.body;){if(!(e=e.parentElement))return!1;if("function"==typeof e.matches?e.matches(t):e.msMatchesSelector(t))return e}}function g(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!e||""===t)return!1;if("none"==t)return T(i)&&i(),!1;var n=x(),s=t.split(" ");o(s,(function(t){h(e,"g"+t)})),a(n,{onElement:e,avoidDuplicate:!1,once:!0,withCallback:function(e,t){o(s,(function(e){d(t,"g"+e)})),T(i)&&i()}})}function v(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(""==t)return e.style.webkitTransform="",e.style.MozTransform="",e.style.msTransform="",e.style.OTransform="",e.style.transform="",!1;e.style.webkitTransform=t,e.style.MozTransform=t,e.style.msTransform=t,e.style.OTransform=t,e.style.transform=t}function f(e){e.style.display="block"}function p(e){e.style.display="none"}function m(e){var t=document.createDocumentFragment(),i=document.createElement("div");for(i.innerHTML=e;i.firstChild;)t.appendChild(i.firstChild);return t}function y(){return{width:window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,height:window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight}}function x(){var e,t=document.createElement("fakeelement"),i={animation:"animationend",OAnimation:"oAnimationEnd",MozAnimation:"animationend",WebkitAnimation:"webkitAnimationEnd"};for(e in i)if(void 0!==t.style[e])return i[e]}function b(e,t,i,n){if(e())t();else{var s;i||(i=100);var l=setInterval((function(){e()&&(clearInterval(l),s&&clearTimeout(s),t())}),i);n&&(s=setTimeout((function(){clearInterval(l)}),n))}}function S(e,t,i){if(I(e))console.error("Inject assets error");else if(T(t)&&(i=t,t=!1),C(t)&&t in window)T(i)&&i();else{var n;if(-1!==e.indexOf(".css")){if((n=document.querySelectorAll('link[href="'+e+'"]'))&&n.length>0)return void(T(i)&&i());var s=document.getElementsByTagName("head")[0],l=s.querySelectorAll('link[rel="stylesheet"]'),o=document.createElement("link");return o.rel="stylesheet",o.type="text/css",o.href=e,o.media="all",l?s.insertBefore(o,l[0]):s.appendChild(o),void(T(i)&&i())}if((n=document.querySelectorAll('script[src="'+e+'"]'))&&n.length>0){if(T(i)){if(C(t))return b((function(){return void 0!==window[t]}),(function(){i()})),!1;i()}}else{var r=document.createElement("script");r.type="text/javascript",r.src=e,r.onload=function(){if(T(i)){if(C(t))return b((function(){return void 0!==window[t]}),(function(){i()})),!1;i()}},document.body.appendChild(r)}}}function w(){return"navigator"in window&&window.navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(Android)|(PlayBook)|(BB10)|(BlackBerry)|(Opera Mini)|(IEMobile)|(webOS)|(MeeGo)/i)}function T(e){return"function"==typeof e}function C(e){return"string"==typeof e}function k(e){return!(!e||!e.nodeType||1!=e.nodeType)}function E(e){return Array.isArray(e)}function A(e){return e&&e.length&&isFinite(e.length)}function L(t){return"object"===e(t)&&null!=t&&!T(t)&&!E(t)}function I(e){return null==e}function O(e,t){return null!==e&&hasOwnProperty.call(e,t)}function P(e){if(L(e)){if(e.keys)return e.keys().length;var t=0;for(var i in e)O(e,i)&&t++;return t}return e.length}function M(e){return!isNaN(parseFloat(e))&&isFinite(e)}function z(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:-1,t=document.querySelectorAll(".gbtn[data-taborder]:not(.disabled)");if(!t.length)return!1;if(1==t.length)return t[0];"string"==typeof e&&(e=parseInt(e));var i=[];o(t,(function(e){i.push(e.getAttribute("data-taborder"))}));var n=Math.max.apply(Math,i.map((function(e){return parseInt(e)}))),s=e<0?1:e+1;s>n&&(s="1");var l=i.filter((function(e){return e>=parseInt(s)})),r=l.sort()[0];return document.querySelector('.gbtn[data-taborder="'.concat(r,'"]'))}function X(e){if(e.events.hasOwnProperty("keyboard"))return!1;e.events.keyboard=a("keydown",{onElement:window,withCallback:function(t,i){var n=(t=t||window.event).keyCode;if(9==n){var s=document.querySelector(".gbtn.focused");if(!s){var l=!(!document.activeElement||!document.activeElement.nodeName)&&document.activeElement.nodeName.toLocaleLowerCase();if("input"==l||"textarea"==l||"button"==l)return}t.preventDefault();var o=document.querySelectorAll(".gbtn[data-taborder]");if(!o||o.length<=0)return;if(!s){var r=z();return void(r&&(r.focus(),h(r,"focused")))}var a=z(s.getAttribute("data-taborder"));d(s,"focused"),a&&(a.focus(),h(a,"focused"))}39==n&&e.nextSlide(),37==n&&e.prevSlide(),27==n&&e.close()}})}function Y(e){return Math.sqrt(e.x*e.x+e.y*e.y)}function q(e,t){var i=function(e,t){var i=Y(e)*Y(t);if(0===i)return 0;var n=function(e,t){return e.x*t.x+e.y*t.y}(e,t)/i;return n>1&&(n=1),Math.acos(n)}(e,t);return function(e,t){return e.x*t.y-t.x*e.y}(e,t)>0&&(i*=-1),180*i/Math.PI}var N=function(){function e(i){t(this,e),this.handlers=[],this.el=i}return n(e,[{key:"add",value:function(e){this.handlers.push(e)}},{key:"del",value:function(e){e||(this.handlers=[]);for(var t=this.handlers.length;t>=0;t--)this.handlers[t]===e&&this.handlers.splice(t,1)}},{key:"dispatch",value:function(){for(var e=0,t=this.handlers.length;e=0)console.log("ignore drag for this touched element",e.target.nodeName.toLowerCase());else{this.now=Date.now(),this.x1=e.touches[0].pageX,this.y1=e.touches[0].pageY,this.delta=this.now-(this.last||this.now),this.touchStart.dispatch(e,this.element),null!==this.preTapPosition.x&&(this.isDoubleTap=this.delta>0&&this.delta<=250&&Math.abs(this.preTapPosition.x-this.x1)<30&&Math.abs(this.preTapPosition.y-this.y1)<30,this.isDoubleTap&&clearTimeout(this.singleTapTimeout)),this.preTapPosition.x=this.x1,this.preTapPosition.y=this.y1,this.last=this.now;var t=this.preV;if(e.touches.length>1){this._cancelLongTap(),this._cancelSingleTap();var i={x:e.touches[1].pageX-this.x1,y:e.touches[1].pageY-this.y1};t.x=i.x,t.y=i.y,this.pinchStartLen=Y(t),this.multipointStart.dispatch(e,this.element)}this._preventTap=!1,this.longTapTimeout=setTimeout(function(){this.longTap.dispatch(e,this.element),this._preventTap=!0}.bind(this),750)}}}},{key:"move",value:function(e){if(e.touches){var t=this.preV,i=e.touches.length,n=e.touches[0].pageX,s=e.touches[0].pageY;if(this.isDoubleTap=!1,i>1){var l=e.touches[1].pageX,o=e.touches[1].pageY,r={x:e.touches[1].pageX-n,y:e.touches[1].pageY-s};null!==t.x&&(this.pinchStartLen>0&&(e.zoom=Y(r)/this.pinchStartLen,this.pinch.dispatch(e,this.element)),e.angle=q(r,t),this.rotate.dispatch(e,this.element)),t.x=r.x,t.y=r.y,null!==this.x2&&null!==this.sx2?(e.deltaX=(n-this.x2+l-this.sx2)/2,e.deltaY=(s-this.y2+o-this.sy2)/2):(e.deltaX=0,e.deltaY=0),this.twoFingerPressMove.dispatch(e,this.element),this.sx2=l,this.sy2=o}else{if(null!==this.x2){e.deltaX=n-this.x2,e.deltaY=s-this.y2;var a=Math.abs(this.x1-this.x2),h=Math.abs(this.y1-this.y2);(a>10||h>10)&&(this._preventTap=!0)}else e.deltaX=0,e.deltaY=0;this.pressMove.dispatch(e,this.element)}this.touchMove.dispatch(e,this.element),this._cancelLongTap(),this.x2=n,this.y2=s,i>1&&e.preventDefault()}}},{key:"end",value:function(e){if(e.changedTouches){this._cancelLongTap();var t=this;e.touches.length<2&&(this.multipointEnd.dispatch(e,this.element),this.sx2=this.sy2=null),this.x2&&Math.abs(this.x1-this.x2)>30||this.y2&&Math.abs(this.y1-this.y2)>30?(e.direction=this._swipeDirection(this.x1,this.x2,this.y1,this.y2),this.swipeTimeout=setTimeout((function(){t.swipe.dispatch(e,t.element)}),0)):(this.tapTimeout=setTimeout((function(){t._preventTap||t.tap.dispatch(e,t.element),t.isDoubleTap&&(t.doubleTap.dispatch(e,t.element),t.isDoubleTap=!1)}),0),t.isDoubleTap||(t.singleTapTimeout=setTimeout((function(){t.singleTap.dispatch(e,t.element)}),250))),this.touchEnd.dispatch(e,this.element),this.preV.x=0,this.preV.y=0,this.zoom=1,this.pinchStartLen=null,this.x1=this.x2=this.y1=this.y2=null}}},{key:"cancelAll",value:function(){this._preventTap=!0,clearTimeout(this.singleTapTimeout),clearTimeout(this.tapTimeout),clearTimeout(this.longTapTimeout),clearTimeout(this.swipeTimeout)}},{key:"cancel",value:function(e){this.cancelAll(),this.touchCancel.dispatch(e,this.element)}},{key:"_cancelLongTap",value:function(){clearTimeout(this.longTapTimeout)}},{key:"_cancelSingleTap",value:function(){clearTimeout(this.singleTapTimeout)}},{key:"_swipeDirection",value:function(e,t,i,n){return Math.abs(e-t)>=Math.abs(i-n)?e-t>0?"Left":"Right":i-n>0?"Up":"Down"}},{key:"on",value:function(e,t){this[e]&&this[e].add(t)}},{key:"off",value:function(e,t){this[e]&&this[e].del(t)}},{key:"destroy",value:function(){return this.singleTapTimeout&&clearTimeout(this.singleTapTimeout),this.tapTimeout&&clearTimeout(this.tapTimeout),this.longTapTimeout&&clearTimeout(this.longTapTimeout),this.swipeTimeout&&clearTimeout(this.swipeTimeout),this.element.removeEventListener("touchstart",this.start),this.element.removeEventListener("touchmove",this.move),this.element.removeEventListener("touchend",this.end),this.element.removeEventListener("touchcancel",this.cancel),this.rotate.del(),this.touchStart.del(),this.multipointStart.del(),this.multipointEnd.del(),this.pinch.del(),this.swipe.del(),this.tap.del(),this.doubleTap.del(),this.longTap.del(),this.singleTap.del(),this.pressMove.del(),this.twoFingerPressMove.del(),this.touchMove.del(),this.touchEnd.del(),this.touchCancel.del(),this.preV=this.pinchStartLen=this.zoom=this.isDoubleTap=this.delta=this.last=this.now=this.tapTimeout=this.singleTapTimeout=this.longTapTimeout=this.swipeTimeout=this.x1=this.x2=this.y1=this.y2=this.preTapPosition=this.rotate=this.touchStart=this.multipointStart=this.multipointEnd=this.pinch=this.swipe=this.tap=this.doubleTap=this.longTap=this.singleTap=this.pressMove=this.touchMove=this.touchEnd=this.touchCancel=this.twoFingerPressMove=null,window.removeEventListener("scroll",this._cancelAllHandler),null}}]),e}();function W(e){var t=function(){var e,t=document.createElement("fakeelement"),i={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(e in i)if(void 0!==t.style[e])return i[e]}(),i=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,n=c(e,"gslide-media")?e:e.querySelector(".gslide-media"),s=u(n,".ginner-container"),l=e.querySelector(".gslide-description");i>769&&(n=s),h(n,"greset"),v(n,"translate3d(0, 0, 0)"),a(t,{onElement:n,once:!0,withCallback:function(e,t){d(n,"greset")}}),n.style.opacity="",l&&(l.style.opacity="")}function B(e){if(e.events.hasOwnProperty("touch"))return!1;var t,i,n,s=y(),l=s.width,o=s.height,r=!1,a=null,g=null,f=null,p=!1,m=1,x=1,b=!1,S=!1,w=null,T=null,C=null,k=null,E=0,A=0,L=!1,I=!1,O={},P={},M=0,z=0,X=document.getElementById("glightbox-slider"),Y=document.querySelector(".goverlay"),q=new _(X,{touchStart:function(t){if(r=!0,(c(t.targetTouches[0].target,"ginner-container")||u(t.targetTouches[0].target,".gslide-desc")||"a"==t.targetTouches[0].target.nodeName.toLowerCase())&&(r=!1),u(t.targetTouches[0].target,".gslide-inline")&&!c(t.targetTouches[0].target.parentNode,"gslide-inline")&&(r=!1),r){if(P=t.targetTouches[0],O.pageX=t.targetTouches[0].pageX,O.pageY=t.targetTouches[0].pageY,M=t.targetTouches[0].clientX,z=t.targetTouches[0].clientY,a=e.activeSlide,g=a.querySelector(".gslide-media"),n=a.querySelector(".gslide-inline"),f=null,c(g,"gslide-image")&&(f=g.querySelector("img")),(window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth)>769&&(g=a.querySelector(".ginner-container")),d(Y,"greset"),t.pageX>20&&t.pageXo){var a=O.pageX-P.pageX;if(Math.abs(a)<=13)return!1}p=!0;var h,d=s.targetTouches[0].clientX,c=s.targetTouches[0].clientY,u=M-d,m=z-c;if(Math.abs(u)>Math.abs(m)?(L=!1,I=!0):(I=!1,L=!0),t=P.pageX-O.pageX,E=100*t/l,i=P.pageY-O.pageY,A=100*i/o,L&&f&&(h=1-Math.abs(i)/o,Y.style.opacity=h,e.settings.touchFollowAxis&&(E=0)),I&&(h=1-Math.abs(t)/l,g.style.opacity=h,e.settings.touchFollowAxis&&(A=0)),!f)return v(g,"translate3d(".concat(E,"%, 0, 0)"));v(g,"translate3d(".concat(E,"%, ").concat(A,"%, 0)"))}},touchEnd:function(){if(r){if(p=!1,S||b)return C=w,void(k=T);var t=Math.abs(parseInt(A)),i=Math.abs(parseInt(E));if(!(t>29&&f))return t<29&&i<25?(h(Y,"greset"),Y.style.opacity=1,W(g)):void 0;e.close()}},multipointEnd:function(){setTimeout((function(){b=!1}),50)},multipointStart:function(){b=!0,m=x||1},pinch:function(e){if(!f||p)return!1;b=!0,f.scaleX=f.scaleY=m*e.zoom;var t=m*e.zoom;if(S=!0,t<=1)return S=!1,t=1,k=null,C=null,w=null,T=null,void f.setAttribute("style","");t>4.5&&(t=4.5),f.style.transform="scale3d(".concat(t,", ").concat(t,", 1)"),x=t},pressMove:function(e){if(S&&!b){var t=P.pageX-O.pageX,i=P.pageY-O.pageY;C&&(t+=C),k&&(i+=k),w=t,T=i;var n="translate3d(".concat(t,"px, ").concat(i,"px, 0)");x&&(n+=" scale3d(".concat(x,", ").concat(x,", 1)")),v(f,n)}},swipe:function(t){if(!S)if(b)b=!1;else{if("Left"==t.direction){if(e.index==e.elements.length-1)return W(g);e.nextSlide()}if("Right"==t.direction){if(0==e.index)return W(g);e.prevSlide()}}}});e.events.touch=q}var H=function(){function e(i,n){var s=this,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(t(this,e),this.img=i,this.slide=n,this.onclose=l,this.img.setZoomEvents)return!1;this.active=!1,this.zoomedIn=!1,this.dragging=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.img.addEventListener("mousedown",(function(e){return s.dragStart(e)}),!1),this.img.addEventListener("mouseup",(function(e){return s.dragEnd(e)}),!1),this.img.addEventListener("mousemove",(function(e){return s.drag(e)}),!1),this.img.addEventListener("click",(function(e){return s.slide.classList.contains("dragging-nav")?(s.zoomOut(),!1):s.zoomedIn?void(s.zoomedIn&&!s.dragging&&s.zoomOut()):s.zoomIn()}),!1),this.img.setZoomEvents=!0}return n(e,[{key:"zoomIn",value:function(){var e=this.widowWidth();if(!(this.zoomedIn||e<=768)){var t=this.img;if(t.setAttribute("data-style",t.getAttribute("style")),t.style.maxWidth=t.naturalWidth+"px",t.style.maxHeight=t.naturalHeight+"px",t.naturalWidth>e){var i=e/2-t.naturalWidth/2;this.setTranslate(this.img.parentNode,i,0)}this.slide.classList.add("zoomed"),this.zoomedIn=!0}}},{key:"zoomOut",value:function(){this.img.parentNode.setAttribute("style",""),this.img.setAttribute("style",this.img.getAttribute("data-style")),this.slide.classList.remove("zoomed"),this.zoomedIn=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.onclose&&"function"==typeof this.onclose&&this.onclose()}},{key:"dragStart",value:function(e){e.preventDefault(),this.zoomedIn?("touchstart"===e.type?(this.initialX=e.touches[0].clientX-this.xOffset,this.initialY=e.touches[0].clientY-this.yOffset):(this.initialX=e.clientX-this.xOffset,this.initialY=e.clientY-this.yOffset),e.target===this.img&&(this.active=!0,this.img.classList.add("dragging"))):this.active=!1}},{key:"dragEnd",value:function(e){var t=this;e.preventDefault(),this.initialX=this.currentX,this.initialY=this.currentY,this.active=!1,setTimeout((function(){t.dragging=!1,t.img.isDragging=!1,t.img.classList.remove("dragging")}),100)}},{key:"drag",value:function(e){this.active&&(e.preventDefault(),"touchmove"===e.type?(this.currentX=e.touches[0].clientX-this.initialX,this.currentY=e.touches[0].clientY-this.initialY):(this.currentX=e.clientX-this.initialX,this.currentY=e.clientY-this.initialY),this.xOffset=this.currentX,this.yOffset=this.currentY,this.img.isDragging=!0,this.dragging=!0,this.setTranslate(this.img,this.currentX,this.currentY))}},{key:"onMove",value:function(e){if(this.zoomedIn){var t=e.clientX-this.img.naturalWidth/2,i=e.clientY-this.img.naturalHeight/2;this.setTranslate(this.img,t,i)}}},{key:"setTranslate",value:function(e,t,i){e.style.transform="translate3d("+t+"px, "+i+"px, 0)"}},{key:"widowWidth",value:function(){return window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth}}]),e}(),V=function(){function e(){var i=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e);var s=n.dragEl,l=n.toleranceX,o=void 0===l?40:l,r=n.toleranceY,a=void 0===r?65:r,h=n.slide,d=void 0===h?null:h,c=n.instance,u=void 0===c?null:c;this.el=s,this.active=!1,this.dragging=!1,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.direction=null,this.lastDirection=null,this.toleranceX=o,this.toleranceY=a,this.toleranceReached=!1,this.dragContainer=this.el,this.slide=d,this.instance=u,this.el.addEventListener("mousedown",(function(e){return i.dragStart(e)}),!1),this.el.addEventListener("mouseup",(function(e){return i.dragEnd(e)}),!1),this.el.addEventListener("mousemove",(function(e){return i.drag(e)}),!1)}return n(e,[{key:"dragStart",value:function(e){if(this.slide.classList.contains("zoomed"))this.active=!1;else{"touchstart"===e.type?(this.initialX=e.touches[0].clientX-this.xOffset,this.initialY=e.touches[0].clientY-this.yOffset):(this.initialX=e.clientX-this.xOffset,this.initialY=e.clientY-this.yOffset);var t=e.target.nodeName.toLowerCase();e.target.classList.contains("nodrag")||u(e.target,".nodrag")||-1!==["input","select","textarea","button","a"].indexOf(t)?this.active=!1:(e.preventDefault(),(e.target===this.el||"img"!==t&&u(e.target,".gslide-inline"))&&(this.active=!0,this.el.classList.add("dragging"),this.dragContainer=u(e.target,".ginner-container")))}}},{key:"dragEnd",value:function(e){var t=this;e&&e.preventDefault(),this.initialX=0,this.initialY=0,this.currentX=null,this.currentY=null,this.initialX=null,this.initialY=null,this.xOffset=0,this.yOffset=0,this.active=!1,this.doSlideChange&&(this.instance.preventOutsideClick=!0,"right"==this.doSlideChange&&this.instance.prevSlide(),"left"==this.doSlideChange&&this.instance.nextSlide()),this.doSlideClose&&this.instance.close(),this.toleranceReached||this.setTranslate(this.dragContainer,0,0,!0),setTimeout((function(){t.instance.preventOutsideClick=!1,t.toleranceReached=!1,t.lastDirection=null,t.dragging=!1,t.el.isDragging=!1,t.el.classList.remove("dragging"),t.slide.classList.remove("dragging-nav"),t.dragContainer.style.transform="",t.dragContainer.style.transition=""}),100)}},{key:"drag",value:function(e){if(this.active){e.preventDefault(),this.slide.classList.add("dragging-nav"),"touchmove"===e.type?(this.currentX=e.touches[0].clientX-this.initialX,this.currentY=e.touches[0].clientY-this.initialY):(this.currentX=e.clientX-this.initialX,this.currentY=e.clientY-this.initialY),this.xOffset=this.currentX,this.yOffset=this.currentY,this.el.isDragging=!0,this.dragging=!0,this.doSlideChange=!1,this.doSlideClose=!1;var t=Math.abs(this.currentX),i=Math.abs(this.currentY);if(t>0&&t>=Math.abs(this.currentY)&&(!this.lastDirection||"x"==this.lastDirection)){this.yOffset=0,this.lastDirection="x",this.setTranslate(this.dragContainer,this.currentX,0);var n=this.shouldChange();if(!this.instance.settings.dragAutoSnap&&n&&(this.doSlideChange=n),this.instance.settings.dragAutoSnap&&n)return this.instance.preventOutsideClick=!0,this.toleranceReached=!0,this.active=!1,this.instance.preventOutsideClick=!0,this.dragEnd(null),"right"==n&&this.instance.prevSlide(),void("left"==n&&this.instance.nextSlide())}if(this.toleranceY>0&&i>0&&i>=t&&(!this.lastDirection||"y"==this.lastDirection)){this.xOffset=0,this.lastDirection="y",this.setTranslate(this.dragContainer,0,this.currentY);var s=this.shouldClose();return!this.instance.settings.dragAutoSnap&&s&&(this.doSlideClose=!0),void(this.instance.settings.dragAutoSnap&&s&&this.instance.close())}}}},{key:"shouldChange",value:function(){var e=!1;if(Math.abs(this.currentX)>=this.toleranceX){var t=this.currentX>0?"right":"left";("left"==t&&this.slide!==this.slide.parentNode.lastChild||"right"==t&&this.slide!==this.slide.parentNode.firstChild)&&(e=t)}return e}},{key:"shouldClose",value:function(){var e=!1;return Math.abs(this.currentY)>=this.toleranceY&&(e=!0),e}},{key:"setTranslate",value:function(e,t,i){var n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e.style.transition=n?"all .2s ease":"",e.style.transform="translate3d(".concat(t,"px, ").concat(i,"px, 0)")}}]),e}();function j(e,t,i,n){var s=e.querySelector(".gslide-media"),l=new Image,o="gSlideTitle_"+i,r="gSlideDesc_"+i;l.addEventListener("load",(function(){T(n)&&n()}),!1),l.src=t.href,""!=t.sizes&&""!=t.srcset&&(l.sizes=t.sizes,l.srcset=t.srcset),l.alt="",I(t.alt)||""===t.alt||(l.alt=t.alt),""!==t.title&&l.setAttribute("aria-labelledby",o),""!==t.description&&l.setAttribute("aria-describedby",r),t.hasOwnProperty("_hasCustomWidth")&&t._hasCustomWidth&&(l.style.width=t.width),t.hasOwnProperty("_hasCustomHeight")&&t._hasCustomHeight&&(l.style.height=t.height),s.insertBefore(l,s.firstChild)}function F(e,t,i,n){var s=this,l=e.querySelector(".ginner-container"),o="gvideo"+i,r=e.querySelector(".gslide-media"),a=this.getAllPlayers();h(l,"gvideo-container"),r.insertBefore(m('
'),r.firstChild);var d=e.querySelector(".gvideo-wrapper");S(this.settings.plyr.css,"Plyr");var c=t.href,u=location.protocol.replace(":",""),g="",v="",f=!1;"file"==u&&(u="http"),r.style.maxWidth=t.width,S(this.settings.plyr.js,"Plyr",(function(){if(c.match(/vimeo\.com\/([0-9]*)/)){var l=/vimeo.*\/(\d+)/i.exec(c);g="vimeo",v=l[1]}if(c.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/)||c.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/)||c.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/)){var r=function(e){var t="";t=void 0!==(e=e.replace(/(>|<)/gi,"").split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/))[2]?(t=e[2].split(/[^0-9a-z_\-]/i))[0]:e;return t}(c);g="youtube",v=r}if(null!==c.match(/\.(mp4|ogg|webm|mov)$/)){g="local";var u='")}var w=f||m('
'));h(d,"".concat(g,"-video gvideo")),d.appendChild(w),d.setAttribute("data-id",o),d.setAttribute("data-index",i);var C=O(s.settings.plyr,"config")?s.settings.plyr.config:{},k=new Plyr("#"+o,C);k.on("ready",(function(e){var t=e.detail.plyr;a[o]=t,T(n)&&n()})),b((function(){return e.querySelector("iframe")&&"true"==e.querySelector("iframe").dataset.ready}),(function(){s.resize(e)})),k.on("enterfullscreen",R),k.on("exitfullscreen",R)}))}function R(e){var t=u(e.target,".gslide-media");"enterfullscreen"==e.type&&h(t,"fullscreen"),"exitfullscreen"==e.type&&d(t,"fullscreen")}function G(e,t,i,n){var s,l=this,o=e.querySelector(".gslide-media"),r=!(!O(t,"href")||!t.href)&&t.href.split("#").pop().trim(),d=!(!O(t,"content")||!t.content)&&t.content;if(d&&(C(d)&&(s=m('
'.concat(d,"
"))),k(d))){"none"==d.style.display&&(d.style.display="block");var c=document.createElement("div");c.className="ginlined-content",c.appendChild(d),s=c}if(r){var u=document.getElementById(r);if(!u)return!1;var g=u.cloneNode(!0);g.style.height=t.height,g.style.maxWidth=t.width,h(g,"ginlined-content"),s=g}if(!s)return console.error("Unable to append inline slide content",t),!1;o.style.height=t.height,o.style.width=t.width,o.appendChild(s),this.events["inlineclose"+r]=a("click",{onElement:o.querySelectorAll(".gtrigger-close"),withCallback:function(e){e.preventDefault(),l.close()}}),T(n)&&n()}function Z(e,t,i,n){var s=e.querySelector(".gslide-media"),l=function(e){var t=e.url,i=e.allow,n=e.callback,s=e.appendTo,l=document.createElement("iframe");return l.className="vimeo-video gvideo",l.src=t,l.style.width="100%",l.style.height="100%",i&&l.setAttribute("allow",i),l.onload=function(){h(l,"node-ready"),T(n)&&n()},s&&s.appendChild(l),l}({url:t.href,callback:n});s.parentNode.style.maxWidth=t.width,s.parentNode.style.height=t.height,s.appendChild(l)}var $=function(){function e(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.defaults={href:"",sizes:"",srcset:"",title:"",type:"",description:"",alt:"",descPosition:"bottom",effect:"",width:"",height:"",content:!1,zoomable:!0,draggable:!0},L(i)&&(this.defaults=l(this.defaults,i))}return n(e,[{key:"sourceType",value:function(e){var t=e;if(null!==(e=e.toLowerCase()).match(/\.(jpeg|jpg|jpe|gif|png|apn|webp|avif|svg)/))return"image";if(e.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/)||e.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/)||e.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/))return"video";if(e.match(/vimeo\.com\/([0-9]*)/))return"video";if(null!==e.match(/\.(mp4|ogg|webm|mov)/))return"video";if(null!==e.match(/\.(mp3|wav|wma|aac|ogg)/))return"audio";if(e.indexOf("#")>-1&&""!==t.split("#").pop().trim())return"inline";return e.indexOf("goajax=true")>-1?"ajax":"external"}},{key:"parseConfig",value:function(e,t){var i=this,n=l({descPosition:t.descPosition},this.defaults);if(L(e)&&!k(e)){O(e,"type")||(O(e,"content")&&e.content?e.type="inline":O(e,"href")&&(e.type=this.sourceType(e.href)));var s=l(n,e);return this.setSize(s,t),s}var r="",a=e.getAttribute("data-glightbox"),h=e.nodeName.toLowerCase();if("a"===h&&(r=e.href),"img"===h&&(r=e.src,n.alt=e.alt),n.href=r,o(n,(function(s,l){O(t,l)&&"width"!==l&&(n[l]=t[l]);var o=e.dataset[l];I(o)||(n[l]=i.sanitizeValue(o))})),n.content&&(n.type="inline"),!n.type&&r&&(n.type=this.sourceType(r)),I(a)){if(!n.title&&"a"==h){var d=e.title;I(d)||""===d||(n.title=d)}if(!n.title&&"img"==h){var c=e.alt;I(c)||""===c||(n.title=c)}}else{var u=[];o(n,(function(e,t){u.push(";\\s?"+t)})),u=u.join("\\s?:|"),""!==a.trim()&&o(n,(function(e,t){var s=a,l=new RegExp("s?"+t+"s?:s?(.*?)("+u+"s?:|$)"),o=s.match(l);if(o&&o.length&&o[1]){var r=o[1].trim().replace(/;\s*$/,"");n[t]=i.sanitizeValue(r)}}))}if(n.description&&"."===n.description.substring(0,1)){var g;try{g=document.querySelector(n.description).innerHTML}catch(e){if(!(e instanceof DOMException))throw e}g&&(n.description=g)}if(!n.description){var v=e.querySelector(".glightbox-desc");v&&(n.description=v.innerHTML)}return this.setSize(n,t,e),this.slideConfig=n,n}},{key:"setSize",value:function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n="video"==e.type?this.checkSize(t.videosWidth):this.checkSize(t.width),s=this.checkSize(t.height);return e.width=O(e,"width")&&""!==e.width?this.checkSize(e.width):n,e.height=O(e,"height")&&""!==e.height?this.checkSize(e.height):s,i&&"image"==e.type&&(e._hasCustomWidth=!!i.dataset.width,e._hasCustomHeight=!!i.dataset.height),e}},{key:"checkSize",value:function(e){return M(e)?"".concat(e,"px"):e}},{key:"sanitizeValue",value:function(e){return"true"!==e&&"false"!==e?e:"true"===e}}]),e}(),U=function(){function e(i,n,s){t(this,e),this.element=i,this.instance=n,this.index=s}return n(e,[{key:"setContent",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(c(t,"loaded"))return!1;var n=this.instance.settings,s=this.slideConfig,l=w();T(n.beforeSlideLoad)&&n.beforeSlideLoad({index:this.index,slide:t,player:!1});var o=s.type,r=s.descPosition,a=t.querySelector(".gslide-media"),d=t.querySelector(".gslide-title"),u=t.querySelector(".gslide-desc"),g=t.querySelector(".gdesc-inner"),v=i,f="gSlideTitle_"+this.index,p="gSlideDesc_"+this.index;if(T(n.afterSlideLoad)&&(v=function(){T(i)&&i(),n.afterSlideLoad({index:e.index,slide:t,player:e.instance.getSlidePlayerInstance(e.index)})}),""==s.title&&""==s.description?g&&g.parentNode.parentNode.removeChild(g.parentNode):(d&&""!==s.title?(d.id=f,d.innerHTML=s.title):d.parentNode.removeChild(d),u&&""!==s.description?(u.id=p,l&&n.moreLength>0?(s.smallDescription=this.slideShortDesc(s.description,n.moreLength,n.moreText),u.innerHTML=s.smallDescription,this.descriptionEvents(u,s)):u.innerHTML=s.description):u.parentNode.removeChild(u),h(a.parentNode,"desc-".concat(r)),h(g.parentNode,"description-".concat(r))),h(a,"gslide-".concat(o)),h(t,"loaded"),"video"!==o){if("external"!==o)return"inline"===o?(G.apply(this.instance,[t,s,this.index,v]),void(s.draggable&&new V({dragEl:t.querySelector(".gslide-inline"),toleranceX:n.dragToleranceX,toleranceY:n.dragToleranceY,slide:t,instance:this.instance}))):void("image"!==o?T(v)&&v():j(t,s,this.index,(function(){var i=t.querySelector("img");s.draggable&&new V({dragEl:i,toleranceX:n.dragToleranceX,toleranceY:n.dragToleranceY,slide:t,instance:e.instance}),s.zoomable&&i.naturalWidth>i.offsetWidth&&(h(i,"zoomable"),new H(i,t,(function(){e.instance.resize()}))),T(v)&&v()})));Z.apply(this,[t,s,this.index,v])}else F.apply(this.instance,[t,s,this.index,v])}},{key:"slideShortDesc",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:50,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=document.createElement("div");n.innerHTML=e;var s=n.innerText,l=i;if((e=s.trim()).length<=t)return e;var o=e.substr(0,t-1);return l?(n=null,o+'... '+i+""):o}},{key:"descriptionEvents",value:function(e,t){var i=this,n=e.querySelector(".desc-more");if(!n)return!1;a("click",{onElement:n,withCallback:function(e,n){e.preventDefault();var s=document.body,l=u(n,".gslide-desc");if(!l)return!1;l.innerHTML=t.description,h(s,"gdesc-open");var o=a("click",{onElement:[s,u(l,".gslide-description")],withCallback:function(e,n){"a"!==e.target.nodeName.toLowerCase()&&(d(s,"gdesc-open"),h(s,"gdesc-closed"),l.innerHTML=t.smallDescription,i.descriptionEvents(l,t),setTimeout((function(){d(s,"gdesc-closed")}),400),o.destroy())}})}})}},{key:"create",value:function(){return m(this.instance.settings.slideHTML)}},{key:"getConfig",value:function(){k(this.element)||this.element.hasOwnProperty("draggable")||(this.element.draggable=this.instance.settings.draggable);var e=new $(this.instance.settings.slideExtraAttributes);return this.slideConfig=e.parseConfig(this.element,this.instance.settings),this.slideConfig}}]),e}(),J=w(),K=null!==w()||void 0!==document.createTouch||"ontouchstart"in window||"onmsgesturechange"in window||navigator.msMaxTouchPoints,Q=document.getElementsByTagName("html")[0],ee={selector:".glightbox",elements:null,skin:"clean",theme:"clean",closeButton:!0,startAt:null,autoplayVideos:!0,autofocusVideos:!0,descPosition:"bottom",width:"900px",height:"506px",videosWidth:"960px",beforeSlideChange:null,afterSlideChange:null,beforeSlideLoad:null,afterSlideLoad:null,slideInserted:null,slideRemoved:null,slideExtraAttributes:null,onOpen:null,onClose:null,loop:!1,zoomable:!0,draggable:!0,dragAutoSnap:!1,dragToleranceX:40,dragToleranceY:65,preload:!0,oneSlidePerOpen:!1,touchNavigation:!0,touchFollowAxis:!0,keyboardNavigation:!0,closeOnOutsideClick:!0,plugins:!1,plyr:{css:"https://cdn.plyr.io/3.6.8/plyr.css",js:"https://cdn.plyr.io/3.6.8/plyr.js",config:{ratio:"16:9",fullscreen:{enabled:!0,iosNative:!0},youtube:{noCookie:!0,rel:0,showinfo:0,iv_load_policy:3},vimeo:{byline:!1,portrait:!1,title:!1,transparent:!1}}},openEffect:"zoom",closeEffect:"zoom",slideEffect:"slide",moreText:"See more",moreLength:60,cssEfects:{fade:{in:"fadeIn",out:"fadeOut"},zoom:{in:"zoomIn",out:"zoomOut"},slide:{in:"slideInRight",out:"slideOutLeft"},slideBack:{in:"slideInLeft",out:"slideOutRight"},none:{in:"none",out:"none"}},svg:{close:'',next:' ',prev:''},slideHTML:'
\n
\n
\n
\n
\n
\n
\n

\n
\n
\n
\n
\n
\n
',lightboxHTML:''},te=function(){function e(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.customOptions=i,this.settings=l(ee,i),this.effectsClasses=this.getAnimationClasses(),this.videoPlayers={},this.apiEvents=[],this.fullElementsList=!1}return n(e,[{key:"init",value:function(){var e=this,t=this.getSelector();t&&(this.baseEvents=a("click",{onElement:t,withCallback:function(t,i){t.preventDefault(),e.open(i)}})),this.elements=this.getElements()}},{key:"open",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if(0==this.elements.length)return!1;this.activeSlide=null,this.prevActiveSlideIndex=null,this.prevActiveSlide=null;var i=M(t)?t:this.settings.startAt;if(k(e)){var n=e.getAttribute("data-gallery");n&&(this.fullElementsList=this.elements,this.elements=this.getGalleryElements(this.elements,n)),I(i)&&(i=this.getElementIndex(e))<0&&(i=0)}M(i)||(i=0),this.build(),g(this.overlay,"none"==this.settings.openEffect?"none":this.settings.cssEfects.fade.in);var s=document.body,l=window.innerWidth-document.documentElement.clientWidth;if(l>0){var o=document.createElement("style");o.type="text/css",o.className="gcss-styles",o.innerText=".gscrollbar-fixer {margin-right: ".concat(l,"px}"),document.head.appendChild(o),h(s,"gscrollbar-fixer")}h(s,"glightbox-open"),h(Q,"glightbox-open"),J&&(h(document.body,"glightbox-mobile"),this.settings.slideEffect="slide"),this.showSlide(i,!0),1==this.elements.length?(h(this.prevButton,"glightbox-button-hidden"),h(this.nextButton,"glightbox-button-hidden")):(d(this.prevButton,"glightbox-button-hidden"),d(this.nextButton,"glightbox-button-hidden")),this.lightboxOpen=!0,this.trigger("open"),T(this.settings.onOpen)&&this.settings.onOpen(),K&&this.settings.touchNavigation&&B(this),this.settings.keyboardNavigation&&X(this)}},{key:"openAt",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;this.open(null,e)}},{key:"showSlide",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];f(this.loader),this.index=parseInt(t);var n=this.slidesContainer.querySelector(".current");n&&d(n,"current"),this.slideAnimateOut();var s=this.slidesContainer.querySelectorAll(".gslide")[t];if(c(s,"loaded"))this.slideAnimateIn(s,i),p(this.loader);else{f(this.loader);var l=this.elements[t],o={index:this.index,slide:s,slideNode:s,slideConfig:l.slideConfig,slideIndex:this.index,trigger:l.node,player:null};this.trigger("slide_before_load",o),l.instance.setContent(s,(function(){p(e.loader),e.resize(),e.slideAnimateIn(s,i),e.trigger("slide_after_load",o)}))}this.slideDescription=s.querySelector(".gslide-description"),this.slideDescriptionContained=this.slideDescription&&c(this.slideDescription.parentNode,"gslide-media"),this.settings.preload&&(this.preloadSlide(t+1),this.preloadSlide(t-1)),this.updateNavigationClasses(),this.activeSlide=s}},{key:"preloadSlide",value:function(e){var t=this;if(e<0||e>this.elements.length-1)return!1;if(I(this.elements[e]))return!1;var i=this.slidesContainer.querySelectorAll(".gslide")[e];if(c(i,"loaded"))return!1;var n=this.elements[e],s=n.type,l={index:e,slide:i,slideNode:i,slideConfig:n.slideConfig,slideIndex:e,trigger:n.node,player:null};this.trigger("slide_before_load",l),"video"==s||"external"==s?setTimeout((function(){n.instance.setContent(i,(function(){t.trigger("slide_after_load",l)}))}),200):n.instance.setContent(i,(function(){t.trigger("slide_after_load",l)}))}},{key:"prevSlide",value:function(){this.goToSlide(this.index-1)}},{key:"nextSlide",value:function(){this.goToSlide(this.index+1)}},{key:"goToSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(this.prevActiveSlide=this.activeSlide,this.prevActiveSlideIndex=this.index,!this.loop()&&(e<0||e>this.elements.length-1))return!1;e<0?e=this.elements.length-1:e>=this.elements.length&&(e=0),this.showSlide(e)}},{key:"insertSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;t<0&&(t=this.elements.length);var i=new U(e,this,t),n=i.getConfig(),s=l({},n),o=i.create(),r=this.elements.length-1;s.index=t,s.node=!1,s.instance=i,s.slideConfig=n,this.elements.splice(t,0,s);var a=null,h=null;if(this.slidesContainer){if(t>r)this.slidesContainer.appendChild(o);else{var d=this.slidesContainer.querySelectorAll(".gslide")[t];this.slidesContainer.insertBefore(o,d)}(this.settings.preload&&0==this.index&&0==t||this.index-1==t||this.index+1==t)&&this.preloadSlide(t),0==this.index&&0==t&&(this.index=1),this.updateNavigationClasses(),a=this.slidesContainer.querySelectorAll(".gslide")[t],h=this.getSlidePlayerInstance(t),s.slideNode=a}this.trigger("slide_inserted",{index:t,slide:a,slideNode:a,slideConfig:n,slideIndex:t,trigger:null,player:h}),T(this.settings.slideInserted)&&this.settings.slideInserted({index:t,slide:a,player:h})}},{key:"removeSlide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:-1;if(e<0||e>this.elements.length-1)return!1;var t=this.slidesContainer&&this.slidesContainer.querySelectorAll(".gslide")[e];t&&(this.getActiveSlideIndex()==e&&(e==this.elements.length-1?this.prevSlide():this.nextSlide()),t.parentNode.removeChild(t)),this.elements.splice(e,1),this.trigger("slide_removed",e),T(this.settings.slideRemoved)&&this.settings.slideRemoved(e)}},{key:"slideAnimateIn",value:function(e,t){var i=this,n=e.querySelector(".gslide-media"),s=e.querySelector(".gslide-description"),l={index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,slideNode:this.prevActiveSlide,slideIndex:this.prevActiveSlide,slideConfig:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].slideConfig,trigger:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].node,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},o={index:this.index,slide:this.activeSlide,slideNode:this.activeSlide,slideConfig:this.elements[this.index].slideConfig,slideIndex:this.index,trigger:this.elements[this.index].node,player:this.getSlidePlayerInstance(this.index)};if(n.offsetWidth>0&&s&&(p(s),s.style.display=""),d(e,this.effectsClasses),t)g(e,this.settings.cssEfects[this.settings.openEffect].in,(function(){i.settings.autoplayVideos&&i.slidePlayerPlay(e),i.trigger("slide_changed",{prev:l,current:o}),T(i.settings.afterSlideChange)&&i.settings.afterSlideChange.apply(i,[l,o])}));else{var r=this.settings.slideEffect,a="none"!==r?this.settings.cssEfects[r].in:r;this.prevActiveSlideIndex>this.index&&"slide"==this.settings.slideEffect&&(a=this.settings.cssEfects.slideBack.in),g(e,a,(function(){i.settings.autoplayVideos&&i.slidePlayerPlay(e),i.trigger("slide_changed",{prev:l,current:o}),T(i.settings.afterSlideChange)&&i.settings.afterSlideChange.apply(i,[l,o])}))}setTimeout((function(){i.resize(e)}),100),h(e,"current")}},{key:"slideAnimateOut",value:function(){if(!this.prevActiveSlide)return!1;var e=this.prevActiveSlide;d(e,this.effectsClasses),h(e,"prev");var t=this.settings.slideEffect,i="none"!==t?this.settings.cssEfects[t].out:t;this.slidePlayerPause(e),this.trigger("slide_before_change",{prev:{index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,slideNode:this.prevActiveSlide,slideIndex:this.prevActiveSlideIndex,slideConfig:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].slideConfig,trigger:I(this.prevActiveSlideIndex)?null:this.elements[this.prevActiveSlideIndex].node,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},current:{index:this.index,slide:this.activeSlide,slideNode:this.activeSlide,slideIndex:this.index,slideConfig:this.elements[this.index].slideConfig,trigger:this.elements[this.index].node,player:this.getSlidePlayerInstance(this.index)}}),T(this.settings.beforeSlideChange)&&this.settings.beforeSlideChange.apply(this,[{index:this.prevActiveSlideIndex,slide:this.prevActiveSlide,player:this.getSlidePlayerInstance(this.prevActiveSlideIndex)},{index:this.index,slide:this.activeSlide,player:this.getSlidePlayerInstance(this.index)}]),this.prevActiveSlideIndex>this.index&&"slide"==this.settings.slideEffect&&(i=this.settings.cssEfects.slideBack.out),g(e,i,(function(){var t=e.querySelector(".ginner-container"),i=e.querySelector(".gslide-media"),n=e.querySelector(".gslide-description");t.style.transform="",i.style.transform="",d(i,"greset"),i.style.opacity="",n&&(n.style.opacity=""),d(e,"prev")}))}},{key:"getAllPlayers",value:function(){return this.videoPlayers}},{key:"getSlidePlayerInstance",value:function(e){var t="gvideo"+e,i=this.getAllPlayers();return!(!O(i,t)||!i[t])&&i[t]}},{key:"stopSlideVideo",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}console.log("stopSlideVideo is deprecated, use slidePlayerPause");var i=this.getSlidePlayerInstance(e);i&&i.playing&&i.pause()}},{key:"slidePlayerPause",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}var i=this.getSlidePlayerInstance(e);i&&i.playing&&i.pause()}},{key:"playSlideVideo",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}console.log("playSlideVideo is deprecated, use slidePlayerPlay");var i=this.getSlidePlayerInstance(e);i&&!i.playing&&i.play()}},{key:"slidePlayerPlay",value:function(e){if(k(e)){var t=e.querySelector(".gvideo-wrapper");t&&(e=t.getAttribute("data-index"))}var i=this.getSlidePlayerInstance(e);i&&!i.playing&&(i.play(),this.settings.autofocusVideos&&i.elements.container.focus())}},{key:"setElements",value:function(e){var t=this;this.settings.elements=!1;var i=[];e&&e.length&&o(e,(function(e,n){var s=new U(e,t,n),o=s.getConfig(),r=l({},o);r.slideConfig=o,r.instance=s,r.index=n,i.push(r)})),this.elements=i,this.lightboxOpen&&(this.slidesContainer.innerHTML="",this.elements.length&&(o(this.elements,(function(){var e=m(t.settings.slideHTML);t.slidesContainer.appendChild(e)})),this.showSlide(0,!0)))}},{key:"getElementIndex",value:function(e){var t=!1;return o(this.elements,(function(i,n){if(O(i,"node")&&i.node==e)return t=n,!0})),t}},{key:"getElements",value:function(){var e=this,t=[];this.elements=this.elements?this.elements:[],!I(this.settings.elements)&&E(this.settings.elements)&&this.settings.elements.length&&o(this.settings.elements,(function(i,n){var s=new U(i,e,n),o=s.getConfig(),r=l({},o);r.node=!1,r.index=n,r.instance=s,r.slideConfig=o,t.push(r)}));var i=!1;return this.getSelector()&&(i=document.querySelectorAll(this.getSelector())),i?(o(i,(function(i,n){var s=new U(i,e,n),o=s.getConfig(),r=l({},o);r.node=i,r.index=n,r.instance=s,r.slideConfig=o,r.gallery=i.getAttribute("data-gallery"),t.push(r)})),t):t}},{key:"getGalleryElements",value:function(e,t){return e.filter((function(e){return e.gallery==t}))}},{key:"getSelector",value:function(){return!this.settings.elements&&(this.settings.selector&&"data-"==this.settings.selector.substring(0,5)?"*[".concat(this.settings.selector,"]"):this.settings.selector)}},{key:"getActiveSlide",value:function(){return this.slidesContainer.querySelectorAll(".gslide")[this.index]}},{key:"getActiveSlideIndex",value:function(){return this.index}},{key:"getAnimationClasses",value:function(){var e=[];for(var t in this.settings.cssEfects)if(this.settings.cssEfects.hasOwnProperty(t)){var i=this.settings.cssEfects[t];e.push("g".concat(i.in)),e.push("g".concat(i.out))}return e.join(" ")}},{key:"build",value:function(){var e=this;if(this.built)return!1;var t=document.body.childNodes,i=[];o(t,(function(e){e.parentNode==document.body&&"#"!==e.nodeName.charAt(0)&&e.hasAttribute&&!e.hasAttribute("aria-hidden")&&(i.push(e),e.setAttribute("aria-hidden","true"))}));var n=O(this.settings.svg,"next")?this.settings.svg.next:"",s=O(this.settings.svg,"prev")?this.settings.svg.prev:"",l=O(this.settings.svg,"close")?this.settings.svg.close:"",r=this.settings.lightboxHTML;r=m(r=(r=(r=r.replace(/{nextSVG}/g,n)).replace(/{prevSVG}/g,s)).replace(/{closeSVG}/g,l)),document.body.appendChild(r);var d=document.getElementById("glightbox-body");this.modal=d;var g=d.querySelector(".gclose");this.prevButton=d.querySelector(".gprev"),this.nextButton=d.querySelector(".gnext"),this.overlay=d.querySelector(".goverlay"),this.loader=d.querySelector(".gloader"),this.slidesContainer=document.getElementById("glightbox-slider"),this.bodyHiddenChildElms=i,this.events={},h(this.modal,"glightbox-"+this.settings.skin),this.settings.closeButton&&g&&(this.events.close=a("click",{onElement:g,withCallback:function(t,i){t.preventDefault(),e.close()}})),g&&!this.settings.closeButton&&g.parentNode.removeChild(g),this.nextButton&&(this.events.next=a("click",{onElement:this.nextButton,withCallback:function(t,i){t.preventDefault(),e.nextSlide()}})),this.prevButton&&(this.events.prev=a("click",{onElement:this.prevButton,withCallback:function(t,i){t.preventDefault(),e.prevSlide()}})),this.settings.closeOnOutsideClick&&(this.events.outClose=a("click",{onElement:d,withCallback:function(t,i){e.preventOutsideClick||c(document.body,"glightbox-mobile")||u(t.target,".ginner-container")||u(t.target,".gbtn")||c(t.target,"gnext")||c(t.target,"gprev")||e.close()}})),o(this.elements,(function(t,i){e.slidesContainer.appendChild(t.instance.create()),t.slideNode=e.slidesContainer.querySelectorAll(".gslide")[i]})),K&&h(document.body,"glightbox-touch"),this.events.resize=a("resize",{onElement:window,withCallback:function(){e.resize()}}),this.built=!0}},{key:"resize",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;if((e=e||this.activeSlide)&&!c(e,"zoomed")){var t=y(),i=e.querySelector(".gvideo-wrapper"),n=e.querySelector(".gslide-image"),s=this.slideDescription,l=t.width,o=t.height;if(l<=768?h(document.body,"glightbox-mobile"):d(document.body,"glightbox-mobile"),i||n){var r=!1;if(s&&(c(s,"description-bottom")||c(s,"description-top"))&&!c(s,"gabsolute")&&(r=!0),n)if(l<=768)n.querySelector("img");else if(r){var a=s.offsetHeight,u=n.querySelector("img");u.setAttribute("style","max-height: calc(100vh - ".concat(a,"px)")),s.setAttribute("style","max-width: ".concat(u.offsetWidth,"px;"))}if(i){var g=O(this.settings.plyr.config,"ratio")?this.settings.plyr.config.ratio:"";if(!g){var v=i.clientWidth,f=i.clientHeight,p=v/f;g="".concat(v/p,":").concat(f/p)}var m=g.split(":"),x=this.settings.videosWidth,b=this.settings.videosWidth,S=(b=M(x)||-1!==x.indexOf("px")?parseInt(x):-1!==x.indexOf("vw")?l*parseInt(x)/100:-1!==x.indexOf("vh")?o*parseInt(x)/100:-1!==x.indexOf("%")?l*parseInt(x)/100:parseInt(i.clientWidth))/(parseInt(m[0])/parseInt(m[1]));if(S=Math.floor(S),r&&(o-=s.offsetHeight),b>l||S>o||ob){var w=i.offsetWidth,T=i.offsetHeight,C=o/T,k={width:w*C,height:T*C};i.parentNode.setAttribute("style","max-width: ".concat(k.width,"px")),r&&s.setAttribute("style","max-width: ".concat(k.width,"px;"))}else i.parentNode.style.maxWidth="".concat(x),r&&s.setAttribute("style","max-width: ".concat(x,";"))}}}}},{key:"reload",value:function(){this.init()}},{key:"updateNavigationClasses",value:function(){var e=this.loop();d(this.nextButton,"disabled"),d(this.prevButton,"disabled"),0==this.index&&this.elements.length-1==0?(h(this.prevButton,"disabled"),h(this.nextButton,"disabled")):0!==this.index||e?this.index!==this.elements.length-1||e||h(this.nextButton,"disabled"):h(this.prevButton,"disabled")}},{key:"loop",value:function(){var e=O(this.settings,"loopAtEnd")?this.settings.loopAtEnd:null;return e=O(this.settings,"loop")?this.settings.loop:e,e}},{key:"close",value:function(){var e=this;if(!this.lightboxOpen){if(this.events){for(var t in this.events)this.events.hasOwnProperty(t)&&this.events[t].destroy();this.events=null}return!1}if(this.closing)return!1;this.closing=!0,this.slidePlayerPause(this.activeSlide),this.fullElementsList&&(this.elements=this.fullElementsList),this.bodyHiddenChildElms.length&&o(this.bodyHiddenChildElms,(function(e){e.removeAttribute("aria-hidden")})),h(this.modal,"glightbox-closing"),g(this.overlay,"none"==this.settings.openEffect?"none":this.settings.cssEfects.fade.out),g(this.activeSlide,this.settings.cssEfects[this.settings.closeEffect].out,(function(){if(e.activeSlide=null,e.prevActiveSlideIndex=null,e.prevActiveSlide=null,e.built=!1,e.events){for(var t in e.events)e.events.hasOwnProperty(t)&&e.events[t].destroy();e.events=null}var i=document.body;d(Q,"glightbox-open"),d(i,"glightbox-open touching gdesc-open glightbox-touch glightbox-mobile gscrollbar-fixer"),e.modal.parentNode.removeChild(e.modal),e.trigger("close"),T(e.settings.onClose)&&e.settings.onClose();var n=document.querySelector(".gcss-styles");n&&n.parentNode.removeChild(n),e.lightboxOpen=!1,e.closing=null}))}},{key:"destroy",value:function(){this.close(),this.clearAllEvents(),this.baseEvents&&this.baseEvents.destroy()}},{key:"on",value:function(e,t){var i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!e||!T(t))throw new TypeError("Event name and callback must be defined");this.apiEvents.push({evt:e,once:i,callback:t})}},{key:"once",value:function(e,t){this.on(e,t,!0)}},{key:"trigger",value:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=[];o(this.apiEvents,(function(t,s){var l=t.evt,o=t.once,r=t.callback;l==e&&(r(i),o&&n.push(s))})),n.length&&o(n,(function(e){return t.apiEvents.splice(e,1)}))}},{key:"clearAllEvents",value:function(){this.apiEvents.splice(0,this.apiEvents.length)}},{key:"version",value:function(){return"3.1.1"}}]),e}();return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=new te(e);return t.init(),t}})); diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.ar.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.ar.min.js new file mode 100755 index 00000000..9b06c26c --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.ar.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ar=function(){this.pipeline.reset(),this.pipeline.add(e.ar.trimmer,e.ar.stopWordFilter,e.ar.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ar.stemmer))},e.ar.wordCharacters="ء-ٛٱـ",e.ar.trimmer=e.trimmerSupport.generateTrimmer(e.ar.wordCharacters),e.Pipeline.registerFunction(e.ar.trimmer,"trimmer-ar"),e.ar.stemmer=function(){var e=this;return e.result=!1,e.preRemoved=!1,e.sufRemoved=!1,e.pre={pre1:"ف ك ب و س ل ن ا ي ت",pre2:"ال لل",pre3:"بال وال فال تال كال ولل",pre4:"فبال كبال وبال وكال"},e.suf={suf1:"ه ك ت ن ا ي",suf2:"نك نه ها وك يا اه ون ين تن تم نا وا ان كم كن ني نن ما هم هن تك ته ات يه",suf3:"تين كهم نيه نهم ونه وها يهم ونا ونك وني وهم تكم تنا تها تني تهم كما كها ناه نكم هنا تان يها",suf4:"كموه ناها ونني ونهم تكما تموه تكاه كماه ناكم ناهم نيها وننا"},e.patterns=JSON.parse('{"pt43":[{"pt":[{"c":"ا","l":1}]},{"pt":[{"c":"ا,ت,ن,ي","l":0}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"و","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ي","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ا","l":2},{"c":"ل","l":3,"m":3}]},{"pt":[{"c":"م","l":0}]}],"pt53":[{"pt":[{"c":"ت","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":3},{"c":"ل","l":3,"m":4},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":3}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ن","l":4}]},{"pt":[{"c":"ت","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"م","l":0},{"c":"و","l":3}]},{"pt":[{"c":"ا","l":1},{"c":"و","l":3}]},{"pt":[{"c":"و","l":1},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"ا","l":2},{"c":"ن","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":1},{"c":"ا","l":3}]},{"pt":[{"c":"ي,ت,ا,ن","l":0},{"c":"ت","l":1}],"mPt":[{"c":"ف","l":0,"m":2},{"c":"ع","l":1,"m":3},{"c":"ا","l":2},{"c":"ل","l":3,"m":4}]},{"pt":[{"c":"ت,ي,ا,ن","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":2},{"c":"ي","l":3}]},{"pt":[{"c":"ا,ي,ت,ن","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ء","l":4}]}],"pt63":[{"pt":[{"c":"ا","l":0},{"c":"ت","l":2},{"c":"ا","l":4}]},{"pt":[{"c":"ا,ت,ن,ي","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"و","l":3}]},{"pt":[{"c":"م","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ي","l":1},{"c":"ي","l":3},{"c":"ا","l":4},{"c":"ء","l":5}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ا","l":4}]}],"pt54":[{"pt":[{"c":"ت","l":0}]},{"pt":[{"c":"ا,ي,ت,ن","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"م","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":2}]}],"pt64":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":1}]}],"pt73":[{"pt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ا","l":5}]}],"pt75":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":5}]}]}'),e.execArray=["cleanWord","removeDiacritics","cleanAlef","removeStopWords","normalizeHamzaAndAlef","removeStartWaw","removePre432","removeEndTaa","wordCheck"],e.stem=function(){var r=0;for(e.result=!1,e.preRemoved=!1,e.sufRemoved=!1;r=0)return!0},e.normalizeHamzaAndAlef=function(){return e.word=e.word.replace("ؤ","ء"),e.word=e.word.replace("ئ","ء"),e.word=e.word.replace(/([\u0627])\1+/gi,"ا"),!1},e.removeEndTaa=function(){return!(e.word.length>2)||(e.word=e.word.replace(/[\u0627]$/,""),e.word=e.word.replace("ة",""),!1)},e.removeStartWaw=function(){return e.word.length>3&&"و"==e.word[0]&&"و"==e.word[1]&&(e.word=e.word.slice(1)),!1},e.removePre432=function(){var r=e.word;if(e.word.length>=7){var t=new RegExp("^("+e.pre.pre4.split(" ").join("|")+")");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=6){var c=new RegExp("^("+e.pre.pre3.split(" ").join("|")+")");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=5){var l=new RegExp("^("+e.pre.pre2.split(" ").join("|")+")");e.word=e.word.replace(l,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.patternCheck=function(r){for(var t=0;t3){var t=new RegExp("^("+e.pre.pre1.split(" ").join("|")+")");e.word=e.word.replace(t,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.removeSuf1=function(){var r=e.word;if(0==e.sufRemoved&&e.word.length>3){var t=new RegExp("("+e.suf.suf1.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.removeSuf432=function(){var r=e.word;if(e.word.length>=6){var t=new RegExp("("+e.suf.suf4.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=5){var c=new RegExp("("+e.suf.suf3.split(" ").join("|")+")$");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=4){var l=new RegExp("("+e.suf.suf2.split(" ").join("|")+")$");e.word=e.word.replace(l,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.wordCheck=function(){for(var r=(e.word,[e.removeSuf432,e.removeSuf1,e.removePre1]),t=0,c=!1;e.word.length>=7&&!e.result&&t=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.de.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.de.min.js new file mode 100755 index 00000000..f3b5c108 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.de.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `German` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!v.eq_s(1,e)||(v.ket=v.cursor,!v.in_grouping(p,97,252)))&&(v.slice_from(r),v.cursor=n,!0)}function i(){for(var r,n,i,s,t=v.cursor;;)if(r=v.cursor,v.bra=r,v.eq_s(1,"ß"))v.ket=v.cursor,v.slice_from("ss");else{if(r>=v.limit)break;v.cursor=r+1}for(v.cursor=t;;)for(n=v.cursor;;){if(i=v.cursor,v.in_grouping(p,97,252)){if(s=v.cursor,v.bra=s,e("u","U",i))break;if(v.cursor=s,e("y","Y",i))break}if(i>=v.limit)return void(v.cursor=n);v.cursor=i+1}}function s(){for(;!v.in_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}for(;!v.out_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}function t(){m=v.limit,l=m;var e=v.cursor+3;0<=e&&e<=v.limit&&(d=e,s()||(m=v.cursor,m=v.limit)return;v.cursor++}}}function c(){return m<=v.cursor}function u(){return l<=v.cursor}function a(){var e,r,n,i,s=v.limit-v.cursor;if(v.ket=v.cursor,(e=v.find_among_b(w,7))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:v.slice_del(),v.ket=v.cursor,v.eq_s_b(1,"s")&&(v.bra=v.cursor,v.eq_s_b(3,"nis")&&v.slice_del());break;case 3:v.in_grouping_b(g,98,116)&&v.slice_del()}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(f,4))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:if(v.in_grouping_b(k,98,116)){var t=v.cursor-3;v.limit_backward<=t&&t<=v.limit&&(v.cursor=t,v.slice_del())}}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(_,8))&&(v.bra=v.cursor,u()))switch(e){case 1:v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"ig")&&(v.bra=v.cursor,r=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-r,u()&&v.slice_del()));break;case 2:n=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-n,v.slice_del());break;case 3:if(v.slice_del(),v.ket=v.cursor,i=v.limit-v.cursor,!v.eq_s_b(2,"er")&&(v.cursor=v.limit-i,!v.eq_s_b(2,"en")))break;v.bra=v.cursor,c()&&v.slice_del();break;case 4:v.slice_del(),v.ket=v.cursor,e=v.find_among_b(b,2),e&&(v.bra=v.cursor,u()&&1==e&&v.slice_del())}}var d,l,m,h=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],w=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],f=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],b=[new r("ig",-1,1),new r("lich",-1,1)],_=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],p=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],g=[117,30,5],k=[117,30,4],v=new n;this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var e=v.cursor;return i(),v.cursor=e,t(),v.limit_backward=e,v.cursor=v.limit,a(),v.cursor=v.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.du.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.du.min.js new file mode 100755 index 00000000..49a0f3f0 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.du.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Dutch` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");console.warn('[Lunr Languages] Please use the "nl" instead of the "du". The "nl" code is the standard code for Dutch language, and "du" will be removed in the next major versions.'),e.du=function(){this.pipeline.reset(),this.pipeline.add(e.du.trimmer,e.du.stopWordFilter,e.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.du.stemmer))},e.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.du.trimmer=e.trimmerSupport.generateTrimmer(e.du.wordCharacters),e.Pipeline.registerFunction(e.du.trimmer,"trimmer-du"),e.du.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e,r,i,o=C.cursor;;){if(C.bra=C.cursor,e=C.find_among(b,11))switch(C.ket=C.cursor,e){case 1:C.slice_from("a");continue;case 2:C.slice_from("e");continue;case 3:C.slice_from("i");continue;case 4:C.slice_from("o");continue;case 5:C.slice_from("u");continue;case 6:if(C.cursor>=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(r=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=r);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=r;else if(n(r))break}else if(n(r))break}function n(e){return C.cursor=e,e>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,f=_,t()||(_=C.cursor,_<3&&(_=3),t()||(f=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var e;;)if(C.bra=C.cursor,e=C.find_among(p,3))switch(C.ket=C.cursor,e){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return f<=C.cursor}function a(){var e=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-e,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var e;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.slice_del(),w=!0,a())))}function m(){var e;u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.eq_s_b(3,"gem")||(C.cursor=C.limit-e,C.slice_del(),a())))}function d(){var e,r,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,e=C.find_among_b(h,5))switch(C.bra=C.cursor,e){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(z,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(r=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-r,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,e=C.find_among_b(k,6))switch(C.bra=C.cursor,e){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(j,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var f,_,w,b=[new r("",-1,6),new r("á",0,1),new r("ä",0,1),new r("é",0,2),new r("ë",0,2),new r("í",0,3),new r("ï",0,3),new r("ó",0,4),new r("ö",0,4),new r("ú",0,5),new r("ü",0,5)],p=[new r("",-1,3),new r("I",0,2),new r("Y",0,1)],g=[new r("dd",-1,-1),new r("kk",-1,-1),new r("tt",-1,-1)],h=[new r("ene",-1,2),new r("se",-1,3),new r("en",-1,2),new r("heden",2,1),new r("s",-1,3)],k=[new r("end",-1,1),new r("ig",-1,2),new r("ing",-1,1),new r("lijk",-1,3),new r("baar",-1,4),new r("bar",-1,5)],v=[new r("aa",-1,-1),new r("ee",-1,-1),new r("oo",-1,-1),new r("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(e){C.setCurrent(e)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var r=C.cursor;return e(),C.cursor=r,o(),C.limit_backward=r,C.cursor=C.limit,d(),C.cursor=C.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.du.stemmer,"stemmer-du"),e.du.stopWordFilter=e.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),e.Pipeline.registerFunction(e.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.el.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.el.min.js new file mode 100755 index 00000000..ace017bd --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.el.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.el=function(){this.pipeline.reset(),void 0===this.searchPipeline&&this.pipeline.add(e.el.trimmer,e.el.normilizer),this.pipeline.add(e.el.stopWordFilter,e.el.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.el.stemmer))},e.el.wordCharacters="A-Za-zΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩωΆάΈέΉήΊίΌόΎύΏώΪΐΫΰΐΰ",e.el.trimmer=e.trimmerSupport.generateTrimmer(e.el.wordCharacters),e.Pipeline.registerFunction(e.el.trimmer,"trimmer-el"),e.el.stemmer=function(){function e(e){return s.test(e)}function t(e){return/[ΑΕΗΙΟΥΩ]$/.test(e)}function r(e){return/[ΑΕΗΙΟΩ]$/.test(e)}function n(n){var s=n;if(n.length<3)return s;if(!e(n))return s;if(i.indexOf(n)>=0)return s;var u=new RegExp("(.*)("+Object.keys(l).join("|")+")$"),o=u.exec(s);return null!==o&&(s=o[1]+l[o[2]]),null!==(o=/^(.+?)(ΑΔΕΣ|ΑΔΩΝ)$/.exec(s))&&(s=o[1],/(ΟΚ|ΜΑΜ|ΜΑΝ|ΜΠΑΜΠ|ΠΑΤΕΡ|ΓΙΑΓΙ|ΝΤΑΝΤ|ΚΥΡ|ΘΕΙ|ΠΕΘΕΡ|ΜΟΥΣΑΜ|ΚΑΠΛΑΜ|ΠΑΡ|ΨΑΡ|ΤΖΟΥΡ|ΤΑΜΠΟΥΡ|ΓΑΛΑΤ|ΦΑΦΛΑΤ)$/.test(o[1])||(s+="ΑΔ")),null!==(o=/^(.+?)(ΕΔΕΣ|ΕΔΩΝ)$/.exec(s))&&(s=o[1],/(ΟΠ|ΙΠ|ΕΜΠ|ΥΠ|ΓΗΠ|ΔΑΠ|ΚΡΑΣΠ|ΜΙΛ)$/.test(o[1])&&(s+="ΕΔ")),null!==(o=/^(.+?)(ΟΥΔΕΣ|ΟΥΔΩΝ)$/.exec(s))&&(s=o[1],/(ΑΡΚ|ΚΑΛΙΑΚ|ΠΕΤΑΛ|ΛΙΧ|ΠΛΕΞ|ΣΚ|Σ|ΦΛ|ΦΡ|ΒΕΛ|ΛΟΥΛ|ΧΝ|ΣΠ|ΤΡΑΓ|ΦΕ)$/.test(o[1])&&(s+="ΟΥΔ")),null!==(o=/^(.+?)(ΕΩΣ|ΕΩΝ|ΕΑΣ|ΕΑ)$/.exec(s))&&(s=o[1],/^(Θ|Δ|ΕΛ|ΓΑΛ|Ν|Π|ΙΔ|ΠΑΡ|ΣΤΕΡ|ΟΡΦ|ΑΝΔΡ|ΑΝΤΡ)$/.test(o[1])&&(s+="Ε")),null!==(o=/^(.+?)(ΕΙΟ|ΕΙΟΣ|ΕΙΟΙ|ΕΙΑ|ΕΙΑΣ|ΕΙΕΣ|ΕΙΟΥ|ΕΙΟΥΣ|ΕΙΩΝ)$/.exec(s))&&o[1].length>4&&(s=o[1]),null!==(o=/^(.+?)(ΙΟΥΣ|ΙΑΣ|ΙΕΣ|ΙΟΣ|ΙΟΥ|ΙΟΙ|ΙΩΝ|ΙΟΝ|ΙΑ|ΙΟ)$/.exec(s))&&(s=o[1],(t(s)||s.length<2||/^(ΑΓ|ΑΓΓΕΛ|ΑΓΡ|ΑΕΡ|ΑΘΛ|ΑΚΟΥΣ|ΑΞ|ΑΣ|Β|ΒΙΒΛ|ΒΥΤ|Γ|ΓΙΑΓ|ΓΩΝ|Δ|ΔΑΝ|ΔΗΛ|ΔΗΜ|ΔΟΚΙΜ|ΕΛ|ΖΑΧΑΡ|ΗΛ|ΗΠ|ΙΔ|ΙΣΚ|ΙΣΤ|ΙΟΝ|ΙΩΝ|ΚΙΜΩΛ|ΚΟΛΟΝ|ΚΟΡ|ΚΤΗΡ|ΚΥΡ|ΛΑΓ|ΛΟΓ|ΜΑΓ|ΜΠΑΝ|ΜΠΡ|ΝΑΥΤ|ΝΟΤ|ΟΠΑΛ|ΟΞ|ΟΡ|ΟΣ|ΠΑΝΑΓ|ΠΑΤΡ|ΠΗΛ|ΠΗΝ|ΠΛΑΙΣ|ΠΟΝΤ|ΡΑΔ|ΡΟΔ|ΣΚ|ΣΚΟΡΠ|ΣΟΥΝ|ΣΠΑΝ|ΣΤΑΔ|ΣΥΡ|ΤΗΛ|ΤΙΜ|ΤΟΚ|ΤΟΠ|ΤΡΟΧ|ΦΙΛ|ΦΩΤ|Χ|ΧΙΛ|ΧΡΩΜ|ΧΩΡ)$/.test(o[1]))&&(s+="Ι"),/^(ΠΑΛ)$/.test(o[1])&&(s+="ΑΙ")),null!==(o=/^(.+?)(ΙΚΟΣ|ΙΚΟΝ|ΙΚΕΙΣ|ΙΚΟΙ|ΙΚΕΣ|ΙΚΟΥΣ|ΙΚΗ|ΙΚΗΣ|ΙΚΟ|ΙΚΑ|ΙΚΟΥ|ΙΚΩΝ|ΙΚΩΣ)$/.exec(s))&&(s=o[1],(t(s)||/^(ΑΔ|ΑΛ|ΑΜΑΝ|ΑΜΕΡ|ΑΜΜΟΧΑΛ|ΑΝΗΘ|ΑΝΤΙΔ|ΑΠΛ|ΑΤΤ|ΑΦΡ|ΒΑΣ|ΒΡΩΜ|ΓΕΝ|ΓΕΡ|Δ|ΔΙΚΑΝ|ΔΥΤ|ΕΙΔ|ΕΝΔ|ΕΞΩΔ|ΗΘ|ΘΕΤ|ΚΑΛΛΙΝ|ΚΑΛΠ|ΚΑΤΑΔ|ΚΟΥΖΙΝ|ΚΡ|ΚΩΔ|ΛΟΓ|Μ|ΜΕΡ|ΜΟΝΑΔ|ΜΟΥΛ|ΜΟΥΣ|ΜΠΑΓΙΑΤ|ΜΠΑΝ|ΜΠΟΛ|ΜΠΟΣ|ΜΥΣΤ|Ν|ΝΙΤ|ΞΙΚ|ΟΠΤ|ΠΑΝ|ΠΕΤΣ|ΠΙΚΑΝΤ|ΠΙΤΣ|ΠΛΑΣΤ|ΠΛΙΑΤΣ|ΠΟΝΤ|ΠΟΣΤΕΛΝ|ΠΡΩΤΟΔ|ΣΕΡΤ|ΣΗΜΑΝΤ|ΣΤΑΤ|ΣΥΝΑΔ|ΣΥΝΟΜΗΛ|ΤΕΛ|ΤΕΧΝ|ΤΡΟΠ|ΤΣΑΜ|ΥΠΟΔ|Φ|ΦΙΛΟΝ|ΦΥΛΟΔ|ΦΥΣ|ΧΑΣ)$/.test(o[1])||/(ΦΟΙΝ)$/.test(o[1]))&&(s+="ΙΚ")),"ΑΓΑΜΕ"===s&&(s="ΑΓΑΜ"),null!==(o=/^(.+?)(ΑΓΑΜΕ|ΗΣΑΜΕ|ΟΥΣΑΜΕ|ΗΚΑΜΕ|ΗΘΗΚΑΜΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΑΜΕ)$/.exec(s))&&(s=o[1],/^(ΑΝΑΠ|ΑΠΟΘ|ΑΠΟΚ|ΑΠΟΣΤ|ΒΟΥΒ|ΞΕΘ|ΟΥΛ|ΠΕΘ|ΠΙΚΡ|ΠΟΤ|ΣΙΧ|Χ)$/.test(o[1])&&(s+="ΑΜ")),null!==(o=/^(.+?)(ΑΓΑΝΕ|ΗΣΑΝΕ|ΟΥΣΑΝΕ|ΙΟΝΤΑΝΕ|ΙΟΤΑΝΕ|ΙΟΥΝΤΑΝΕ|ΟΝΤΑΝΕ|ΟΤΑΝΕ|ΟΥΝΤΑΝΕ|ΗΚΑΝΕ|ΗΘΗΚΑΝΕ)$/.exec(s))&&(s=o[1],/^(ΤΡ|ΤΣ)$/.test(o[1])&&(s+="ΑΓΑΝ")),null!==(o=/^(.+?)(ΑΝΕ)$/.exec(s))&&(s=o[1],(r(s)||/^(ΒΕΤΕΡ|ΒΟΥΛΚ|ΒΡΑΧΜ|Γ|ΔΡΑΔΟΥΜ|Θ|ΚΑΛΠΟΥΖ|ΚΑΣΤΕΛ|ΚΟΡΜΟΡ|ΛΑΟΠΛ|ΜΩΑΜΕΘ|Μ|ΜΟΥΣΟΥΛΜΑΝ|ΟΥΛ|Π|ΠΕΛΕΚ|ΠΛ|ΠΟΛΙΣ|ΠΟΡΤΟΛ|ΣΑΡΑΚΑΤΣ|ΣΟΥΛΤ|ΤΣΑΡΛΑΤ|ΟΡΦ|ΤΣΙΓΓ|ΤΣΟΠ|ΦΩΤΟΣΤΕΦ|Χ|ΨΥΧΟΠΛ|ΑΓ|ΟΡΦ|ΓΑΛ|ΓΕΡ|ΔΕΚ|ΔΙΠΛ|ΑΜΕΡΙΚΑΝ|ΟΥΡ|ΠΙΘ|ΠΟΥΡΙΤ|Σ|ΖΩΝΤ|ΙΚ|ΚΑΣΤ|ΚΟΠ|ΛΙΧ|ΛΟΥΘΗΡ|ΜΑΙΝΤ|ΜΕΛ|ΣΙΓ|ΣΠ|ΣΤΕΓ|ΤΡΑΓ|ΤΣΑΓ|Φ|ΕΡ|ΑΔΑΠ|ΑΘΙΓΓ|ΑΜΗΧ|ΑΝΙΚ|ΑΝΟΡΓ|ΑΠΗΓ|ΑΠΙΘ|ΑΤΣΙΓΓ|ΒΑΣ|ΒΑΣΚ|ΒΑΘΥΓΑΛ|ΒΙΟΜΗΧ|ΒΡΑΧΥΚ|ΔΙΑΤ|ΔΙΑΦ|ΕΝΟΡΓ|ΘΥΣ|ΚΑΠΝΟΒΙΟΜΗΧ|ΚΑΤΑΓΑΛ|ΚΛΙΒ|ΚΟΙΛΑΡΦ|ΛΙΒ|ΜΕΓΛΟΒΙΟΜΗΧ|ΜΙΚΡΟΒΙΟΜΗΧ|ΝΤΑΒ|ΞΗΡΟΚΛΙΒ|ΟΛΙΓΟΔΑΜ|ΟΛΟΓΑΛ|ΠΕΝΤΑΡΦ|ΠΕΡΗΦ|ΠΕΡΙΤΡ|ΠΛΑΤ|ΠΟΛΥΔΑΠ|ΠΟΛΥΜΗΧ|ΣΤΕΦ|ΤΑΒ|ΤΕΤ|ΥΠΕΡΗΦ|ΥΠΟΚΟΠ|ΧΑΜΗΛΟΔΑΠ|ΨΗΛΟΤΑΒ)$/.test(o[1]))&&(s+="ΑΝ")),null!==(o=/^(.+?)(ΗΣΕΤΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΕΤΕ)$/.exec(s))&&(s=o[1],(r(s)||/(ΟΔ|ΑΙΡ|ΦΟΡ|ΤΑΘ|ΔΙΑΘ|ΣΧ|ΕΝΔ|ΕΥΡ|ΤΙΘ|ΥΠΕΡΘ|ΡΑΘ|ΕΝΘ|ΡΟΘ|ΣΘ|ΠΥΡ|ΑΙΝ|ΣΥΝΔ|ΣΥΝ|ΣΥΝΘ|ΧΩΡ|ΠΟΝ|ΒΡ|ΚΑΘ|ΕΥΘ|ΕΚΘ|ΝΕΤ|ΡΟΝ|ΑΡΚ|ΒΑΡ|ΒΟΛ|ΩΦΕΛ)$/.test(o[1])||/^(ΑΒΑΡ|ΒΕΝ|ΕΝΑΡ|ΑΒΡ|ΑΔ|ΑΘ|ΑΝ|ΑΠΛ|ΒΑΡΟΝ|ΝΤΡ|ΣΚ|ΚΟΠ|ΜΠΟΡ|ΝΙΦ|ΠΑΓ|ΠΑΡΑΚΑΛ|ΣΕΡΠ|ΣΚΕΛ|ΣΥΡΦ|ΤΟΚ|Υ|Δ|ΕΜ|ΘΑΡΡ|Θ)$/.test(o[1]))&&(s+="ΕΤ")),null!==(o=/^(.+?)(ΟΝΤΑΣ|ΩΝΤΑΣ)$/.exec(s))&&(s=o[1],/^ΑΡΧ$/.test(o[1])&&(s+="ΟΝΤ"),/ΚΡΕ$/.test(o[1])&&(s+="ΩΝΤ")),null!==(o=/^(.+?)(ΟΜΑΣΤΕ|ΙΟΜΑΣΤΕ)$/.exec(s))&&(s=o[1],/^ΟΝ$/.test(o[1])&&(s+="ΟΜΑΣΤ")),null!==(o=/^(.+?)(ΙΕΣΤΕ)$/.exec(s))&&(s=o[1],/^(Π|ΑΠ|ΣΥΜΠ|ΑΣΥΜΠ|ΑΚΑΤΑΠ|ΑΜΕΤΑΜΦ)$/.test(o[1])&&(s+="ΙΕΣΤ")),null!==(o=/^(.+?)(ΕΣΤΕ)$/.exec(s))&&(s=o[1],/^(ΑΛ|ΑΡ|ΕΚΤΕΛ|Ζ|Μ|Ξ|ΠΑΡΑΚΑΛ|ΠΡΟ|ΝΙΣ)$/.test(o[1])&&(s+="ΕΣΤ")),null!==(o=/^(.+?)(ΗΘΗΚΑ|ΗΘΗΚΕΣ|ΗΘΗΚΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΗΚΑ|ΗΚΕΣ|ΗΚΕ)$/.exec(s))&&(s=o[1],(/(ΣΚΩΛ|ΣΚΟΥΛ|ΝΑΡΘ|ΣΦ|ΟΘ|ΠΙΘ)$/.test(o[1])||/^(ΔΙΑΘ|Θ|ΠΑΡΑΚΑΤΑΘ|ΠΡΟΣΘ|ΣΥΝΘ)$/.test(o[1]))&&(s+="ΗΚ")),null!==(o=/^(.+?)(ΟΥΣΑ|ΟΥΣΕΣ|ΟΥΣΕ)$/.exec(s))&&(s=o[1],(t(s)||/^(ΦΑΡΜΑΚ|ΧΑΔ|ΑΓΚ|ΑΝΑΡΡ|ΒΡΟΜ|ΕΚΛΙΠ|ΛΑΜΠΙΔ|ΛΕΧ|Μ|ΠΑΤ|Ρ|Λ|ΜΕΔ|ΜΕΣΑΖ|ΥΠΟΤΕΙΝ|ΑΜ|ΑΙΘ|ΑΝΗΚ|ΔΕΣΠΟΖ|ΕΝΔΙΑΦΕΡ)$/.test(o[1])||/(ΠΟΔΑΡ|ΒΛΕΠ|ΠΑΝΤΑΧ|ΦΡΥΔ|ΜΑΝΤΙΛ|ΜΑΛΛ|ΚΥΜΑΤ|ΛΑΧ|ΛΗΓ|ΦΑΓ|ΟΜ|ΠΡΩΤ)$/.test(o[1]))&&(s+="ΟΥΣ")),null!==(o=/^(.+?)(ΑΓΑ|ΑΓΕΣ|ΑΓΕ)$/.exec(s))&&(s=o[1],(/^(ΑΒΑΣΤ|ΠΟΛΥΦ|ΑΔΗΦ|ΠΑΜΦ|Ρ|ΑΣΠ|ΑΦ|ΑΜΑΛ|ΑΜΑΛΛΙ|ΑΝΥΣΤ|ΑΠΕΡ|ΑΣΠΑΡ|ΑΧΑΡ|ΔΕΡΒΕΝ|ΔΡΟΣΟΠ|ΞΕΦ|ΝΕΟΠ|ΝΟΜΟΤ|ΟΛΟΠ|ΟΜΟΤ|ΠΡΟΣΤ|ΠΡΟΣΩΠΟΠ|ΣΥΜΠ|ΣΥΝΤ|Τ|ΥΠΟΤ|ΧΑΡ|ΑΕΙΠ|ΑΙΜΟΣΤ|ΑΝΥΠ|ΑΠΟΤ|ΑΡΤΙΠ|ΔΙΑΤ|ΕΝ|ΕΠΙΤ|ΚΡΟΚΑΛΟΠ|ΣΙΔΗΡΟΠ|Λ|ΝΑΥ|ΟΥΛΑΜ|ΟΥΡ|Π|ΤΡ|Μ)$/.test(o[1])||/(ΟΦ|ΠΕΛ|ΧΟΡΤ|ΛΛ|ΣΦ|ΡΠ|ΦΡ|ΠΡ|ΛΟΧ|ΣΜΗΝ)$/.test(o[1])&&!/^(ΨΟΦ|ΝΑΥΛΟΧ)$/.test(o[1])||/(ΚΟΛΛ)$/.test(o[1]))&&(s+="ΑΓ")),null!==(o=/^(.+?)(ΗΣΕ|ΗΣΟΥ|ΗΣΑ)$/.exec(s))&&(s=o[1],/^(Ν|ΧΕΡΣΟΝ|ΔΩΔΕΚΑΝ|ΕΡΗΜΟΝ|ΜΕΓΑΛΟΝ|ΕΠΤΑΝ|Ι)$/.test(o[1])&&(s+="ΗΣ")),null!==(o=/^(.+?)(ΗΣΤΕ)$/.exec(s))&&(s=o[1],/^(ΑΣΒ|ΣΒ|ΑΧΡ|ΧΡ|ΑΠΛ|ΑΕΙΜΝ|ΔΥΣΧΡ|ΕΥΧΡ|ΚΟΙΝΟΧΡ|ΠΑΛΙΜΨ)$/.test(o[1])&&(s+="ΗΣΤ")),null!==(o=/^(.+?)(ΟΥΝΕ|ΗΣΟΥΝΕ|ΗΘΟΥΝΕ)$/.exec(s))&&(s=o[1],/^(Ν|Ρ|ΣΠΙ|ΣΤΡΑΒΟΜΟΥΤΣ|ΚΑΚΟΜΟΥΤΣ|ΕΞΩΝ)$/.test(o[1])&&(s+="ΟΥΝ")),null!==(o=/^(.+?)(ΟΥΜΕ|ΗΣΟΥΜΕ|ΗΘΟΥΜΕ)$/.exec(s))&&(s=o[1],/^(ΠΑΡΑΣΟΥΣ|Φ|Χ|ΩΡΙΟΠΛ|ΑΖ|ΑΛΛΟΣΟΥΣ|ΑΣΟΥΣ)$/.test(o[1])&&(s+="ΟΥΜ")),null!=(o=/^(.+?)(ΜΑΤΟΙ|ΜΑΤΟΥΣ|ΜΑΤΟ|ΜΑΤΑ|ΜΑΤΩΣ|ΜΑΤΩΝ|ΜΑΤΟΣ|ΜΑΤΕΣ|ΜΑΤΗ|ΜΑΤΗΣ|ΜΑΤΟΥ)$/.exec(s))&&(s=o[1]+"Μ",/^(ΓΡΑΜ)$/.test(o[1])?s+="Α":/^(ΓΕ|ΣΤΑ)$/.test(o[1])&&(s+="ΑΤ")),null!==(o=/^(.+?)(ΟΥΑ)$/.exec(s))&&(s=o[1]+"ΟΥ"),n.length===s.length&&null!==(o=/^(.+?)(Α|ΑΓΑΤΕ|ΑΓΑΝ|ΑΕΙ|ΑΜΑΙ|ΑΝ|ΑΣ|ΑΣΑΙ|ΑΤΑΙ|ΑΩ|Ε|ΕΙ|ΕΙΣ|ΕΙΤΕ|ΕΣΑΙ|ΕΣ|ΕΤΑΙ|Ι|ΙΕΜΑΙ|ΙΕΜΑΣΤΕ|ΙΕΤΑΙ|ΙΕΣΑΙ|ΙΕΣΑΣΤΕ|ΙΟΜΑΣΤΑΝ|ΙΟΜΟΥΝ|ΙΟΜΟΥΝΑ|ΙΟΝΤΑΝ|ΙΟΝΤΟΥΣΑΝ|ΙΟΣΑΣΤΑΝ|ΙΟΣΑΣΤΕ|ΙΟΣΟΥΝ|ΙΟΣΟΥΝΑ|ΙΟΤΑΝ|ΙΟΥΜΑ|ΙΟΥΜΑΣΤΕ|ΙΟΥΝΤΑΙ|ΙΟΥΝΤΑΝ|Η|ΗΔΕΣ|ΗΔΩΝ|ΗΘΕΙ|ΗΘΕΙΣ|ΗΘΕΙΤΕ|ΗΘΗΚΑΤΕ|ΗΘΗΚΑΝ|ΗΘΟΥΝ|ΗΘΩ|ΗΚΑΤΕ|ΗΚΑΝ|ΗΣ|ΗΣΑΝ|ΗΣΑΤΕ|ΗΣΕΙ|ΗΣΕΣ|ΗΣΟΥΝ|ΗΣΩ|Ο|ΟΙ|ΟΜΑΙ|ΟΜΑΣΤΑΝ|ΟΜΟΥΝ|ΟΜΟΥΝΑ|ΟΝΤΑΙ|ΟΝΤΑΝ|ΟΝΤΟΥΣΑΝ|ΟΣ|ΟΣΑΣΤΑΝ|ΟΣΑΣΤΕ|ΟΣΟΥΝ|ΟΣΟΥΝΑ|ΟΤΑΝ|ΟΥ|ΟΥΜΑΙ|ΟΥΜΑΣΤΕ|ΟΥΝ|ΟΥΝΤΑΙ|ΟΥΝΤΑΝ|ΟΥΣ|ΟΥΣΑΝ|ΟΥΣΑΤΕ|Υ||ΥΑ|ΥΣ|Ω|ΩΝ|ΟΙΣ)$/.exec(s))&&(s=o[1]),null!=(o=/^(.+?)(ΕΣΤΕΡ|ΕΣΤΑΤ|ΟΤΕΡ|ΟΤΑΤ|ΥΤΕΡ|ΥΤΑΤ|ΩΤΕΡ|ΩΤΑΤ)$/.exec(s))&&(/^(ΕΞ|ΕΣ|ΑΝ|ΚΑΤ|Κ|ΠΡ)$/.test(o[1])||(s=o[1]),/^(ΚΑ|Μ|ΕΛΕ|ΛΕ|ΔΕ)$/.test(o[1])&&(s+="ΥΤ")),s}var l={"ΦΑΓΙΑ":"ΦΑ","ΦΑΓΙΟΥ":"ΦΑ","ΦΑΓΙΩΝ":"ΦΑ","ΣΚΑΓΙΑ":"ΣΚΑ","ΣΚΑΓΙΟΥ":"ΣΚΑ","ΣΚΑΓΙΩΝ":"ΣΚΑ","ΣΟΓΙΟΥ":"ΣΟ","ΣΟΓΙΑ":"ΣΟ","ΣΟΓΙΩΝ":"ΣΟ","ΤΑΤΟΓΙΑ":"ΤΑΤΟ","ΤΑΤΟΓΙΟΥ":"ΤΑΤΟ","ΤΑΤΟΓΙΩΝ":"ΤΑΤΟ","ΚΡΕΑΣ":"ΚΡΕ","ΚΡΕΑΤΟΣ":"ΚΡΕ","ΚΡΕΑΤΑ":"ΚΡΕ","ΚΡΕΑΤΩΝ":"ΚΡΕ","ΠΕΡΑΣ":"ΠΕΡ","ΠΕΡΑΤΟΣ":"ΠΕΡ","ΠΕΡΑΤΑ":"ΠΕΡ","ΠΕΡΑΤΩΝ":"ΠΕΡ","ΤΕΡΑΣ":"ΤΕΡ","ΤΕΡΑΤΟΣ":"ΤΕΡ","ΤΕΡΑΤΑ":"ΤΕΡ","ΤΕΡΑΤΩΝ":"ΤΕΡ","ΦΩΣ":"ΦΩ","ΦΩΤΟΣ":"ΦΩ","ΦΩΤΑ":"ΦΩ","ΦΩΤΩΝ":"ΦΩ","ΚΑΘΕΣΤΩΣ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΟΣ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΑ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΩΝ":"ΚΑΘΕΣΤ","ΓΕΓΟΝΟΣ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΟΣ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΑ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΩΝ":"ΓΕΓΟΝ","ΕΥΑ":"ΕΥ"},i=["ΑΚΡΙΒΩΣ","ΑΛΑ","ΑΛΛΑ","ΑΛΛΙΩΣ","ΑΛΛΟΤΕ","ΑΜΑ","ΑΝΩ","ΑΝΑ","ΑΝΑΜΕΣΑ","ΑΝΑΜΕΤΑΞΥ","ΑΝΕΥ","ΑΝΤΙ","ΑΝΤΙΠΕΡΑ","ΑΝΤΙΟ","ΑΞΑΦΝΑ","ΑΠΟ","ΑΠΟΨΕ","ΑΡΑ","ΑΡΑΓΕ","ΑΥΡΙΟ","ΑΦΟΙ","ΑΦΟΥ","ΑΦΟΤΟΥ","ΒΡΕ","ΓΕΙΑ","ΓΙΑ","ΓΙΑΤΙ","ΓΡΑΜΜΑ","ΔΕΗ","ΔΕΝ","ΔΗΛΑΔΗ","ΔΙΧΩΣ","ΔΥΟ","ΕΑΝ","ΕΓΩ","ΕΔΩ","ΕΔΑ","ΕΙΘΕ","ΕΙΜΑΙ","ΕΙΜΑΣΤΕ","ΕΙΣΑΙ","ΕΙΣΑΣΤΕ","ΕΙΝΑΙ","ΕΙΣΤΕ","ΕΙΤΕ","ΕΚΕΙ","ΕΚΟ","ΕΛΑ","ΕΜΑΣ","ΕΜΕΙΣ","ΕΝΤΕΛΩΣ","ΕΝΤΟΣ","ΕΝΤΩΜΕΤΑΞΥ","ΕΝΩ","ΕΞΙ","ΕΞΙΣΟΥ","ΕΞΗΣ","ΕΞΩ","ΕΟΚ","ΕΠΑΝΩ","ΕΠΕΙΔΗ","ΕΠΕΙΤΑ","ΕΠΙ","ΕΠΙΣΗΣ","ΕΠΟΜΕΝΩΣ","ΕΠΤΑ","ΕΣΑΣ","ΕΣΕΙΣ","ΕΣΤΩ","ΕΣΥ","ΕΣΩ","ΕΤΣΙ","ΕΥΓΕ","ΕΦΕ","ΕΦΕΞΗΣ","ΕΧΤΕΣ","ΕΩΣ","ΗΔΗ","ΗΜΙ","ΗΠΑ","ΗΤΟΙ","ΘΕΣ","ΙΔΙΩΣ","ΙΔΗ","ΙΚΑ","ΙΣΩΣ","ΚΑΘΕ","ΚΑΘΕΤΙ","ΚΑΘΟΛΟΥ","ΚΑΘΩΣ","ΚΑΙ","ΚΑΝ","ΚΑΠΟΤΕ","ΚΑΠΟΥ","ΚΑΤΑ","ΚΑΤΙ","ΚΑΤΟΠΙΝ","ΚΑΤΩ","ΚΕΙ","ΚΙΧ","ΚΚΕ","ΚΟΛΑΝ","ΚΥΡΙΩΣ","ΚΩΣ","ΜΑΚΑΡΙ","ΜΑΛΙΣΤΑ","ΜΑΛΛΟΝ","ΜΑΙ","ΜΑΟ","ΜΑΟΥΣ","ΜΑΣ","ΜΕΘΑΥΡΙΟ","ΜΕΣ","ΜΕΣΑ","ΜΕΤΑ","ΜΕΤΑΞΥ","ΜΕΧΡΙ","ΜΗΔΕ","ΜΗΝ","ΜΗΠΩΣ","ΜΗΤΕ","ΜΙΑ","ΜΙΑΣ","ΜΙΣ","ΜΜΕ","ΜΟΛΟΝΟΤΙ","ΜΟΥ","ΜΠΑ","ΜΠΑΣ","ΜΠΟΥΦΑΝ","ΜΠΡΟΣ","ΝΑΙ","ΝΕΣ","ΝΤΑ","ΝΤΕ","ΞΑΝΑ","ΟΗΕ","ΟΚΤΩ","ΟΜΩΣ","ΟΝΕ","ΟΠΑ","ΟΠΟΥ","ΟΠΩΣ","ΟΣΟ","ΟΤΑΝ","ΟΤΕ","ΟΤΙ","ΟΥΤΕ","ΟΧΙ","ΠΑΛΙ","ΠΑΝ","ΠΑΝΟ","ΠΑΝΤΟΤΕ","ΠΑΝΤΟΥ","ΠΑΝΤΩΣ","ΠΑΝΩ","ΠΑΡΑ","ΠΕΡΑ","ΠΕΡΙ","ΠΕΡΙΠΟΥ","ΠΙΑ","ΠΙΟ","ΠΙΣΩ","ΠΛΑΙ","ΠΛΕΟΝ","ΠΛΗΝ","ΠΟΤΕ","ΠΟΥ","ΠΡΟ","ΠΡΟΣ","ΠΡΟΧΤΕΣ","ΠΡΟΧΘΕΣ","ΡΟΔΙ","ΠΩΣ","ΣΑΙ","ΣΑΣ","ΣΑΝ","ΣΕΙΣ","ΣΙΑ","ΣΚΙ","ΣΟΙ","ΣΟΥ","ΣΡΙ","ΣΥΝ","ΣΥΝΑΜΑ","ΣΧΕΔΟΝ","ΤΑΔΕ","ΤΑΞΙ","ΤΑΧΑ","ΤΕΙ","ΤΗΝ","ΤΗΣ","ΤΙΠΟΤΑ","ΤΙΠΟΤΕ","ΤΙΣ","ΤΟΝ","ΤΟΤΕ","ΤΟΥ","ΤΟΥΣ","ΤΣΑ","ΤΣΕ","ΤΣΙ","ΤΣΟΥ","ΤΩΝ","ΥΠΟ","ΥΠΟΨΗ","ΥΠΟΨΙΝ","ΥΣΤΕΡΑ","ΦΕΤΟΣ","ΦΙΣ","ΦΠΑ","ΧΑΦ","ΧΘΕΣ","ΧΤΕΣ","ΧΩΡΙΣ","ΩΣ","ΩΣΑΝ","ΩΣΟΤΟΥ","ΩΣΠΟΥ","ΩΣΤΕ","ΩΣΤΟΣΟ"],s=new RegExp("^[ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ]+$");return function(e){return"function"==typeof e.update?e.update(function(e){return n(e.toUpperCase()).toLowerCase()}):n(e.toUpperCase()).toLowerCase()}}(),e.Pipeline.registerFunction(e.el.stemmer,"stemmer-el"),e.el.stopWordFilter=e.generateStopWordFilter("αλλα αν αντι απο αυτα αυτεσ αυτη αυτο αυτοι αυτοσ αυτουσ αυτων για δε δεν εαν ειμαι ειμαστε ειναι εισαι ειστε εκεινα εκεινεσ εκεινη εκεινο εκεινοι εκεινοσ εκεινουσ εκεινων ενω επι η θα ισωσ κ και κατα κι μα με μετα μη μην να ο οι ομωσ οπωσ οσο οτι παρα ποια ποιεσ ποιο ποιοι ποιοσ ποιουσ ποιων που προσ πωσ σε στη στην στο στον τα την τησ το τον τοτε του των ωσ".split(" ")),e.Pipeline.registerFunction(e.el.stopWordFilter,"stopWordFilter-el"),e.el.normilizer=function(){var e={"Ά":"Α","ά":"α","Έ":"Ε","έ":"ε","Ή":"Η","ή":"η","Ί":"Ι","ί":"ι","Ό":"Ο","ο":"ο","Ύ":"Υ","ύ":"υ","Ώ":"Ω","ώ":"ω","Ϊ":"Ι","ϊ":"ι","Ϋ":"Υ","ϋ":"υ","ΐ":"ι","ΰ":"υ"};return function(t){if("function"==typeof t.update)return t.update(function(t){for(var r="",n=0;n=A.limit)return!0;A.cursor++}return!1}return!0}function n(){if(A.in_grouping(x,97,252)){var s=A.cursor;if(e()){if(A.cursor=s,!A.in_grouping(x,97,252))return!0;for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!0;A.cursor++}}return!1}return!0}function i(){var s,r=A.cursor;if(n()){if(A.cursor=r,!A.out_grouping(x,97,252))return;if(s=A.cursor,e()){if(A.cursor=s,!A.in_grouping(x,97,252)||A.cursor>=A.limit)return;A.cursor++}}g=A.cursor}function a(){for(;!A.in_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}return!0}function t(){var e=A.cursor;g=A.limit,p=g,v=g,i(),A.cursor=e,a()&&(p=A.cursor,a()&&(v=A.cursor))}function o(){for(var e;;){if(A.bra=A.cursor,e=A.find_among(k,6))switch(A.ket=A.cursor,e){case 1:A.slice_from("a");continue;case 2:A.slice_from("e");continue;case 3:A.slice_from("i");continue;case 4:A.slice_from("o");continue;case 5:A.slice_from("u");continue;case 6:if(A.cursor>=A.limit)break;A.cursor++;continue}break}}function u(){return g<=A.cursor}function w(){return p<=A.cursor}function c(){return v<=A.cursor}function m(){var e;if(A.ket=A.cursor,A.find_among_b(y,13)&&(A.bra=A.cursor,(e=A.find_among_b(q,11))&&u()))switch(e){case 1:A.bra=A.cursor,A.slice_from("iendo");break;case 2:A.bra=A.cursor,A.slice_from("ando");break;case 3:A.bra=A.cursor,A.slice_from("ar");break;case 4:A.bra=A.cursor,A.slice_from("er");break;case 5:A.bra=A.cursor,A.slice_from("ir");break;case 6:A.slice_del();break;case 7:A.eq_s_b(1,"u")&&A.slice_del()}}function l(e,s){if(!c())return!0;A.slice_del(),A.ket=A.cursor;var r=A.find_among_b(e,s);return r&&(A.bra=A.cursor,1==r&&c()&&A.slice_del()),!1}function d(e){return!c()||(A.slice_del(),A.ket=A.cursor,A.eq_s_b(2,e)&&(A.bra=A.cursor,c()&&A.slice_del()),!1)}function b(){var e;if(A.ket=A.cursor,e=A.find_among_b(S,46)){switch(A.bra=A.cursor,e){case 1:if(!c())return!1;A.slice_del();break;case 2:if(d("ic"))return!1;break;case 3:if(!c())return!1;A.slice_from("log");break;case 4:if(!c())return!1;A.slice_from("u");break;case 5:if(!c())return!1;A.slice_from("ente");break;case 6:if(!w())return!1;A.slice_del(),A.ket=A.cursor,e=A.find_among_b(C,4),e&&(A.bra=A.cursor,c()&&(A.slice_del(),1==e&&(A.ket=A.cursor,A.eq_s_b(2,"at")&&(A.bra=A.cursor,c()&&A.slice_del()))));break;case 7:if(l(P,3))return!1;break;case 8:if(l(F,3))return!1;break;case 9:if(d("at"))return!1}return!0}return!1}function f(){var e,s;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(W,12),A.limit_backward=s,e)){if(A.bra=A.cursor,1==e){if(!A.eq_s_b(1,"u"))return!1;A.slice_del()}return!0}return!1}function _(){var e,s,r,n;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(L,96),A.limit_backward=s,e))switch(A.bra=A.cursor,e){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"u")?(n=A.limit-A.cursor,A.eq_s_b(1,"g")?A.cursor=A.limit-n:A.cursor=A.limit-r):A.cursor=A.limit-r,A.bra=A.cursor;case 2:A.slice_del()}}function h(){var e,s;if(A.ket=A.cursor,e=A.find_among_b(z,8))switch(A.bra=A.cursor,e){case 1:u()&&A.slice_del();break;case 2:u()&&(A.slice_del(),A.ket=A.cursor,A.eq_s_b(1,"u")&&(A.bra=A.cursor,s=A.limit-A.cursor,A.eq_s_b(1,"g")&&(A.cursor=A.limit-s,u()&&A.slice_del())))}}var v,p,g,k=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],y=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],q=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],C=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],P=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],F=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],S=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],W=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],L=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],z=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],x=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],A=new r;this.setCurrent=function(e){A.setCurrent(e)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return t(),A.limit_backward=e,A.cursor=A.limit,m(),A.cursor=A.limit,b()||(A.cursor=A.limit,f()||(A.cursor=A.limit,_())),A.cursor=A.limit,h(),A.cursor=A.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.fi.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.fi.min.js new file mode 100755 index 00000000..29f5dfce --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.fi.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Finnish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=function(){var e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){function i(){f=A.limit,d=f,n()||(f=A.cursor,n()||(d=A.cursor))}function n(){for(var i;;){if(i=A.cursor,A.in_grouping(W,97,246))break;if(A.cursor=i,i>=A.limit)return!0;A.cursor++}for(A.cursor=i;!A.out_grouping(W,97,246);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}function t(){return d<=A.cursor}function s(){var i,e;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(h,10)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.in_grouping_b(x,97,246))return;break;case 2:if(!t())return}A.slice_del()}else A.limit_backward=e}function o(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(v,9))switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"k")||(A.cursor=A.limit-r,A.slice_del());break;case 2:A.slice_del(),A.ket=A.cursor,A.eq_s_b(3,"kse")&&(A.bra=A.cursor,A.slice_from("ksi"));break;case 3:A.slice_del();break;case 4:A.find_among_b(p,6)&&A.slice_del();break;case 5:A.find_among_b(g,6)&&A.slice_del();break;case 6:A.find_among_b(j,2)&&A.slice_del()}else A.limit_backward=e}function l(){return A.find_among_b(q,7)}function a(){return A.eq_s_b(1,"i")&&A.in_grouping_b(L,97,246)}function u(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(C,30)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.eq_s_b(1,"a"))return;break;case 2:case 9:if(!A.eq_s_b(1,"e"))return;break;case 3:if(!A.eq_s_b(1,"i"))return;break;case 4:if(!A.eq_s_b(1,"o"))return;break;case 5:if(!A.eq_s_b(1,"ä"))return;break;case 6:if(!A.eq_s_b(1,"ö"))return;break;case 7:if(r=A.limit-A.cursor,!l()&&(A.cursor=A.limit-r,!A.eq_s_b(2,"ie"))){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward){A.cursor=A.limit-r;break}A.cursor--,A.bra=A.cursor;break;case 8:if(!A.in_grouping_b(W,97,246)||!A.out_grouping_b(W,97,246))return}A.slice_del(),k=!0}else A.limit_backward=e}function c(){var i,e,r;if(A.cursor>=d)if(e=A.limit_backward,A.limit_backward=d,A.ket=A.cursor,i=A.find_among_b(P,14)){if(A.bra=A.cursor,A.limit_backward=e,1==i){if(r=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-r}A.slice_del()}else A.limit_backward=e}function m(){var i;A.cursor>=f&&(i=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.find_among_b(F,2)?(A.bra=A.cursor,A.limit_backward=i,A.slice_del()):A.limit_backward=i)}function w(){var i,e,r,n,t,s;if(A.cursor>=f){if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.eq_s_b(1,"t")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.in_grouping_b(W,97,246)&&(A.cursor=A.limit-r,A.slice_del(),A.limit_backward=e,n=A.limit-A.cursor,A.cursor>=d&&(A.cursor=d,t=A.limit_backward,A.limit_backward=A.cursor,A.cursor=A.limit-n,A.ket=A.cursor,i=A.find_among_b(S,2))))){if(A.bra=A.cursor,A.limit_backward=t,1==i){if(s=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-s}return void A.slice_del()}A.limit_backward=e}}function _(){var i,e,r,n;if(A.cursor>=f){for(i=A.limit_backward,A.limit_backward=f,e=A.limit-A.cursor,l()&&(A.cursor=A.limit-e,A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.in_grouping_b(y,97,228)&&(A.bra=A.cursor,A.out_grouping_b(W,97,246)&&A.slice_del()),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"j")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.eq_s_b(1,"o")?A.slice_del():(A.cursor=A.limit-r,A.eq_s_b(1,"u")&&A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"o")&&(A.bra=A.cursor,A.eq_s_b(1,"j")&&A.slice_del()),A.cursor=A.limit-e,A.limit_backward=i;;){if(n=A.limit-A.cursor,A.out_grouping_b(W,97,246)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return;A.cursor--}A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,b=A.slice_to(),A.eq_v_b(b)&&A.slice_del())}}var k,b,d,f,h=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],p=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],g=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],j=[new e("lle",-1,-1),new e("ine",-1,-1)],v=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],q=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],C=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,a),new e("seen",11,-1,l),new e("hen",11,2),new e("tten",11,-1,a),new e("hin",11,3),new e("siin",11,-1,a),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],P=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],F=[new e("i",-1,-1),new e("j",-1,-1)],S=[new e("mma",-1,1),new e("imma",0,-1)],y=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],W=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],x=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],A=new r;this.setCurrent=function(i){A.setCurrent(i)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return i(),k=!1,A.limit_backward=e,A.cursor=A.limit,s(),A.cursor=A.limit,o(),A.cursor=A.limit,u(),A.cursor=A.limit,c(),A.cursor=A.limit,k?(m(),A.cursor=A.limit):(A.cursor=A.limit,w(),A.cursor=A.limit),_(),!0}};return function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}}(),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.fr.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.fr.min.js new file mode 100755 index 00000000..68cd0094 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.fr.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `French` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,s){return!(!W.eq_s(1,e)||(W.ket=W.cursor,!W.in_grouping(F,97,251)))&&(W.slice_from(r),W.cursor=s,!0)}function i(e,r,s){return!!W.eq_s(1,e)&&(W.ket=W.cursor,W.slice_from(r),W.cursor=s,!0)}function n(){for(var r,s;;){if(r=W.cursor,W.in_grouping(F,97,251)){if(W.bra=W.cursor,s=W.cursor,e("u","U",r))continue;if(W.cursor=s,e("i","I",r))continue;if(W.cursor=s,i("y","Y",r))continue}if(W.cursor=r,W.bra=r,!e("y","Y",r)){if(W.cursor=r,W.eq_s(1,"q")&&(W.bra=W.cursor,i("u","U",r)))continue;if(W.cursor=r,r>=W.limit)return;W.cursor++}}}function t(){for(;!W.in_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}for(;!W.out_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}return!1}function u(){var e=W.cursor;if(q=W.limit,g=q,p=q,W.in_grouping(F,97,251)&&W.in_grouping(F,97,251)&&W.cursor=W.limit){W.cursor=q;break}W.cursor++}while(!W.in_grouping(F,97,251))}q=W.cursor,W.cursor=e,t()||(g=W.cursor,t()||(p=W.cursor))}function o(){for(var e,r;;){if(r=W.cursor,W.bra=r,!(e=W.find_among(h,4)))break;switch(W.ket=W.cursor,e){case 1:W.slice_from("i");break;case 2:W.slice_from("u");break;case 3:W.slice_from("y");break;case 4:if(W.cursor>=W.limit)return;W.cursor++}}}function c(){return q<=W.cursor}function a(){return g<=W.cursor}function l(){return p<=W.cursor}function w(){var e,r;if(W.ket=W.cursor,e=W.find_among_b(C,43)){switch(W.bra=W.cursor,e){case 1:if(!l())return!1;W.slice_del();break;case 2:if(!l())return!1;W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")&&(W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU"));break;case 3:if(!l())return!1;W.slice_from("log");break;case 4:if(!l())return!1;W.slice_from("u");break;case 5:if(!l())return!1;W.slice_from("ent");break;case 6:if(!c())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(z,6))switch(W.bra=W.cursor,e){case 1:l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&W.slice_del()));break;case 2:l()?W.slice_del():a()&&W.slice_from("eux");break;case 3:l()&&W.slice_del();break;case 4:c()&&W.slice_from("i")}break;case 7:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(y,3))switch(W.bra=W.cursor,e){case 1:l()?W.slice_del():W.slice_from("abl");break;case 2:l()?W.slice_del():W.slice_from("iqU");break;case 3:l()&&W.slice_del()}break;case 8:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")))){W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU");break}break;case 9:W.slice_from("eau");break;case 10:if(!a())return!1;W.slice_from("al");break;case 11:if(l())W.slice_del();else{if(!a())return!1;W.slice_from("eux")}break;case 12:if(!a()||!W.out_grouping_b(F,97,251))return!1;W.slice_del();break;case 13:return c()&&W.slice_from("ant"),!1;case 14:return c()&&W.slice_from("ent"),!1;case 15:return r=W.limit-W.cursor,W.in_grouping_b(F,97,251)&&c()&&(W.cursor=W.limit-r,W.slice_del()),!1}return!0}return!1}function f(){var e,r;if(W.cursor=q){if(s=W.limit_backward,W.limit_backward=q,W.ket=W.cursor,e=W.find_among_b(P,7))switch(W.bra=W.cursor,e){case 1:if(l()){if(i=W.limit-W.cursor,!W.eq_s_b(1,"s")&&(W.cursor=W.limit-i,!W.eq_s_b(1,"t")))break;W.slice_del()}break;case 2:W.slice_from("i");break;case 3:W.slice_del();break;case 4:W.eq_s_b(2,"gu")&&W.slice_del()}W.limit_backward=s}}function b(){var e=W.limit-W.cursor;W.find_among_b(U,5)&&(W.cursor=W.limit-e,W.ket=W.cursor,W.cursor>W.limit_backward&&(W.cursor--,W.bra=W.cursor,W.slice_del()))}function d(){for(var e,r=1;W.out_grouping_b(F,97,251);)r--;if(r<=0){if(W.ket=W.cursor,e=W.limit-W.cursor,!W.eq_s_b(1,"é")&&(W.cursor=W.limit-e,!W.eq_s_b(1,"è")))return;W.bra=W.cursor,W.slice_from("e")}}function k(){if(!w()&&(W.cursor=W.limit,!f()&&(W.cursor=W.limit,!m())))return W.cursor=W.limit,void _();W.cursor=W.limit,W.ket=W.cursor,W.eq_s_b(1,"Y")?(W.bra=W.cursor,W.slice_from("i")):(W.cursor=W.limit,W.eq_s_b(1,"ç")&&(W.bra=W.cursor,W.slice_from("c")))}var p,g,q,v=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],h=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],z=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],y=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],C=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],x=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],I=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],P=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],U=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],F=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],S=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],W=new s;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){var e=W.cursor;return n(),W.cursor=e,u(),W.limit_backward=e,W.cursor=W.limit,k(),W.cursor=W.limit,b(),W.cursor=W.limit,d(),W.cursor=W.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.he.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.he.min.js new file mode 100755 index 00000000..b863d3ea --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.he.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.he=function(){this.pipeline.reset(),this.pipeline.add(e.he.trimmer,e.he.stopWordFilter,e.he.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.he.stemmer))},e.he.wordCharacters="֑-״א-תa-zA-Za-zA-Z0-90-9",e.he.trimmer=e.trimmerSupport.generateTrimmer(e.he.wordCharacters),e.Pipeline.registerFunction(e.he.trimmer,"trimmer-he"),e.he.stemmer=function(){var e=this;return e.result=!1,e.preRemoved=!1,e.sufRemoved=!1,e.pre={pre1:"ה ו י ת",pre2:"ב כ ל מ ש כש",pre3:"הב הכ הל המ הש בש לכ",pre4:"וב וכ ול ומ וש",pre5:"מה שה כל",pre6:"מב מכ מל ממ מש",pre7:"בה בו בי בת כה כו כי כת לה לו לי לת",pre8:"ובה ובו ובי ובת וכה וכו וכי וכת ולה ולו ולי ולת"},e.suf={suf1:"ך כ ם ן נ",suf2:"ים ות וך וכ ום ון ונ הם הן יכ יך ינ ים",suf3:"תי תך תכ תם תן תנ",suf4:"ותי ותך ותכ ותם ותן ותנ",suf5:"נו כם כן הם הן",suf6:"ונו וכם וכן והם והן",suf7:"תכם תכן תנו תהם תהן",suf8:"הוא היא הם הן אני אתה את אנו אתם אתן",suf9:"ני נו כי כו כם כן תי תך תכ תם תן",suf10:"י ך כ ם ן נ ת"},e.patterns=JSON.parse('{"hebrewPatterns": [{"pt1": [{"c": "ה", "l": 0}]}, {"pt2": [{"c": "ו", "l": 0}]}, {"pt3": [{"c": "י", "l": 0}]}, {"pt4": [{"c": "ת", "l": 0}]}, {"pt5": [{"c": "מ", "l": 0}]}, {"pt6": [{"c": "ל", "l": 0}]}, {"pt7": [{"c": "ב", "l": 0}]}, {"pt8": [{"c": "כ", "l": 0}]}, {"pt9": [{"c": "ש", "l": 0}]}, {"pt10": [{"c": "כש", "l": 0}]}, {"pt11": [{"c": "בה", "l": 0}]}, {"pt12": [{"c": "וב", "l": 0}]}, {"pt13": [{"c": "וכ", "l": 0}]}, {"pt14": [{"c": "ול", "l": 0}]}, {"pt15": [{"c": "ומ", "l": 0}]}, {"pt16": [{"c": "וש", "l": 0}]}, {"pt17": [{"c": "הב", "l": 0}]}, {"pt18": [{"c": "הכ", "l": 0}]}, {"pt19": [{"c": "הל", "l": 0}]}, {"pt20": [{"c": "המ", "l": 0}]}, {"pt21": [{"c": "הש", "l": 0}]}, {"pt22": [{"c": "מה", "l": 0}]}, {"pt23": [{"c": "שה", "l": 0}]}, {"pt24": [{"c": "כל", "l": 0}]}]}'),e.execArray=["cleanWord","removeDiacritics","removeStopWords","normalizeHebrewCharacters"],e.stem=function(){var r=0;for(e.result=!1,e.preRemoved=!1,e.sufRemoved=!1;r=0)return!0},e.normalizeHebrewCharacters=function(){return e.word=e.word.replace("ך","כ"),e.word=e.word.replace("ם","מ"),e.word=e.word.replace("ן","נ"),e.word=e.word.replace("ף","פ"),e.word=e.word.replace("ץ","צ"),!1},function(r){return"function"==typeof r.update?r.update(function(r){return e.setCurrent(r),e.stem(),e.getCurrent()}):(e.setCurrent(r),e.stem(),e.getCurrent())}}(),e.Pipeline.registerFunction(e.he.stemmer,"stemmer-he"),e.he.stopWordFilter=e.generateStopWordFilter("אבל או אולי אותו אותי אותך אותם אותן אותנו אז אחר אחרות אחרי אחריכן אחרים אחרת אי איזה איך אין איפה אל אלה אלו אם אנחנו אני אף אפשר את אתה אתכם אתכן אתם אתן באיזה באיזו בגלל בין בלבד בעבור בעזרת בכל בכן בלי במידה במקום שבו ברוב בשביל בשעה ש בתוך גם דרך הוא היא היה היי היכן היתה היתי הם הן הנה הסיבה שבגללה הרי ואילו ואת זאת זה זות יהיה יוכל יוכלו יותר מדי יכול יכולה יכולות יכולים יכל יכלה יכלו יש כאן כאשר כולם כולן כזה כי כיצד כך כל כלל כמו כן כפי כש לא לאו לאיזותך לאן לבין לה להיות להם להן לו לזה לזות לי לך לכם לכן למה למעלה למעלה מ למטה למטה מ למעט למקום שבו למרות לנו לעבר לעיכן לפיכך לפני מאד מאחורי מאיזו סיבה מאין מאיפה מבלי מבעד מדוע מה מהיכן מול מחוץ מי מידע מכאן מכל מכן מלבד מן מנין מסוגל מעט מעטים מעל מצד מקום בו מתחת מתי נגד נגר נו עד עז על עלי עליו עליה עליהם עליך עלינו עם עצמה עצמהם עצמהן עצמו עצמי עצמם עצמן עצמנו פה רק שוב של שלה שלהם שלהן שלו שלי שלך שלכה שלכם שלכן שלנו שם תהיה תחת".split(" ")),e.Pipeline.registerFunction(e.he.stopWordFilter,"stopWordFilter-he")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.hi.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.hi.min.js new file mode 100755 index 00000000..7dbc4140 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.hi.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var t=i.toString().toLowerCase().replace(/^\s+/,"");return r.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.hu.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.hu.min.js new file mode 100755 index 00000000..ed9d909f --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.hu.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Hungarian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,n=L.cursor;if(d=L.limit,L.in_grouping(W,97,252))for(;;){if(e=L.cursor,L.out_grouping(W,97,252))return L.cursor=e,L.find_among(g,8)||(L.cursor=e,e=L.limit)return void(d=e);L.cursor++}if(L.cursor=n,L.out_grouping(W,97,252)){for(;!L.in_grouping(W,97,252);){if(L.cursor>=L.limit)return;L.cursor++}d=L.cursor}}function i(){return d<=L.cursor}function a(){var e;if(L.ket=L.cursor,(e=L.find_among_b(h,2))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e")}}function t(){var e=L.limit-L.cursor;return!!L.find_among_b(p,23)&&(L.cursor=L.limit-e,!0)}function s(){if(L.cursor>L.limit_backward){L.cursor--,L.ket=L.cursor;var e=L.cursor-1;L.limit_backward<=e&&e<=L.limit&&(L.cursor=e,L.bra=e,L.slice_del())}}function c(){var e;if(L.ket=L.cursor,(e=L.find_among_b(_,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function o(){L.ket=L.cursor,L.find_among_b(v,44)&&(L.bra=L.cursor,i()&&(L.slice_del(),a()))}function w(){var e;if(L.ket=L.cursor,(e=L.find_among_b(z,3))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("e");break;case 2:case 3:L.slice_from("a")}}function l(){var e;if(L.ket=L.cursor,(e=L.find_among_b(y,6))&&(L.bra=L.cursor,i()))switch(e){case 1:case 2:L.slice_del();break;case 3:L.slice_from("a");break;case 4:L.slice_from("e")}}function u(){var e;if(L.ket=L.cursor,(e=L.find_among_b(j,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function m(){var e;if(L.ket=L.cursor,(e=L.find_among_b(C,7))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:L.slice_del()}}function k(){var e;if(L.ket=L.cursor,(e=L.find_among_b(P,12))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 9:L.slice_del();break;case 2:case 5:case 8:L.slice_from("e");break;case 3:case 6:L.slice_from("a")}}function f(){var e;if(L.ket=L.cursor,(e=L.find_among_b(F,31))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:L.slice_del();break;case 2:case 5:case 10:case 14:case 19:L.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:L.slice_from("e")}}function b(){var e;if(L.ket=L.cursor,(e=L.find_among_b(S,42))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:L.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:L.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:L.slice_from("e")}}var d,g=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],h=[new n("á",-1,1),new n("é",-1,2)],p=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],_=[new n("al",-1,1),new n("el",-1,2)],v=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],z=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],y=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],j=[new n("á",-1,1),new n("é",-1,2)],C=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],P=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],F=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],S=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var n=L.cursor;return e(),L.limit_backward=n,L.cursor=L.limit,c(),L.cursor=L.limit,o(),L.cursor=L.limit,w(),L.cursor=L.limit,l(),L.cursor=L.limit,u(),L.cursor=L.limit,k(),L.cursor=L.limit,f(),L.cursor=L.limit,b(),L.cursor=L.limit,m(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.hy.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.hy.min.js new file mode 100755 index 00000000..b37f7929 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.hy.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hy=function(){this.pipeline.reset(),this.pipeline.add(e.hy.trimmer,e.hy.stopWordFilter)},e.hy.wordCharacters="[A-Za-z԰-֏ff-ﭏ]",e.hy.trimmer=e.trimmerSupport.generateTrimmer(e.hy.wordCharacters),e.Pipeline.registerFunction(e.hy.trimmer,"trimmer-hy"),e.hy.stopWordFilter=e.generateStopWordFilter("դու և եք էիր էիք հետո նաև նրանք որը վրա է որ պիտի են այս մեջ ն իր ու ի այդ որոնք այն կամ էր մի ես համար այլ իսկ էին ենք հետ ին թ էինք մենք նրա նա դուք եմ էի ըստ որպես ում".split(" ")),e.Pipeline.registerFunction(e.hy.stopWordFilter,"stopWordFilter-hy"),e.hy.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}(),e.Pipeline.registerFunction(e.hy.stemmer,"stemmer-hy")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.it.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.it.min.js new file mode 100755 index 00000000..344b6a3c --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.it.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Italian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!x.eq_s(1,e)||(x.ket=x.cursor,!x.in_grouping(L,97,249)))&&(x.slice_from(r),x.cursor=n,!0)}function i(){for(var r,n,i,o,t=x.cursor;;){if(x.bra=x.cursor,r=x.find_among(h,7))switch(x.ket=x.cursor,r){case 1:x.slice_from("à");continue;case 2:x.slice_from("è");continue;case 3:x.slice_from("ì");continue;case 4:x.slice_from("ò");continue;case 5:x.slice_from("ù");continue;case 6:x.slice_from("qU");continue;case 7:if(x.cursor>=x.limit)break;x.cursor++;continue}break}for(x.cursor=t;;)for(n=x.cursor;;){if(i=x.cursor,x.in_grouping(L,97,249)){if(x.bra=x.cursor,o=x.cursor,e("u","U",i))break;if(x.cursor=o,e("i","I",i))break}if(x.cursor=i,x.cursor>=x.limit)return void(x.cursor=n);x.cursor++}}function o(e){if(x.cursor=e,!x.in_grouping(L,97,249))return!1;for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function t(){if(x.in_grouping(L,97,249)){var e=x.cursor;if(x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return o(e);x.cursor++}return!0}return o(e)}return!1}function s(){var e,r=x.cursor;if(!t()){if(x.cursor=r,!x.out_grouping(L,97,249))return;if(e=x.cursor,x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return x.cursor=e,void(x.in_grouping(L,97,249)&&x.cursor=x.limit)return;x.cursor++}k=x.cursor}function a(){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function u(){var e=x.cursor;k=x.limit,p=k,g=k,s(),x.cursor=e,a()&&(p=x.cursor,a()&&(g=x.cursor))}function c(){for(var e;;){if(x.bra=x.cursor,!(e=x.find_among(q,3)))break;switch(x.ket=x.cursor,e){case 1:x.slice_from("i");break;case 2:x.slice_from("u");break;case 3:if(x.cursor>=x.limit)return;x.cursor++}}}function w(){return k<=x.cursor}function l(){return p<=x.cursor}function m(){return g<=x.cursor}function f(){var e;if(x.ket=x.cursor,x.find_among_b(C,37)&&(x.bra=x.cursor,(e=x.find_among_b(z,5))&&w()))switch(e){case 1:x.slice_del();break;case 2:x.slice_from("e")}}function v(){var e;if(x.ket=x.cursor,!(e=x.find_among_b(S,51)))return!1;switch(x.bra=x.cursor,e){case 1:if(!m())return!1;x.slice_del();break;case 2:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del());break;case 3:if(!m())return!1;x.slice_from("log");break;case 4:if(!m())return!1;x.slice_from("u");break;case 5:if(!m())return!1;x.slice_from("ente");break;case 6:if(!w())return!1;x.slice_del();break;case 7:if(!l())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(P,4),e&&(x.bra=x.cursor,m()&&(x.slice_del(),1==e&&(x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&x.slice_del()))));break;case 8:if(!m())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(F,3),e&&(x.bra=x.cursor,1==e&&m()&&x.slice_del());break;case 9:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del())))}return!0}function b(){var e,r;x.cursor>=k&&(r=x.limit_backward,x.limit_backward=k,x.ket=x.cursor,e=x.find_among_b(W,87),e&&(x.bra=x.cursor,1==e&&x.slice_del()),x.limit_backward=r)}function d(){var e=x.limit-x.cursor;if(x.ket=x.cursor,x.in_grouping_b(y,97,242)&&(x.bra=x.cursor,w()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(1,"i")&&(x.bra=x.cursor,w()))))return void x.slice_del();x.cursor=x.limit-e}function _(){d(),x.ket=x.cursor,x.eq_s_b(1,"h")&&(x.bra=x.cursor,x.in_grouping_b(U,99,103)&&w()&&x.slice_del())}var g,p,k,h=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],q=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],C=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],z=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],P=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],F=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],S=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],W=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],y=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],U=[17],x=new n;this.setCurrent=function(e){x.setCurrent(e)},this.getCurrent=function(){return x.getCurrent()},this.stem=function(){var e=x.cursor;return i(),x.cursor=e,u(),x.limit_backward=e,x.cursor=x.limit,f(),x.cursor=x.limit,v()||(x.cursor=x.limit,b()),x.cursor=x.limit,_(),x.cursor=x.limit_backward,c(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.ja.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.ja.min.js new file mode 100755 index 00000000..5f254ebe --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.ja.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(e=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=e);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=e;else if(n(e))break}else if(n(e))break}function n(r){return C.cursor=r,r>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,d=_,t()||(_=C.cursor,_<3&&(_=3),t()||(d=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var r;;)if(C.bra=C.cursor,r=C.find_among(p,3))switch(C.ket=C.cursor,r){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return d<=C.cursor}function a(){var r=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-r,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var r;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.slice_del(),w=!0,a())))}function m(){var r;u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.eq_s_b(3,"gem")||(C.cursor=C.limit-r,C.slice_del(),a())))}function f(){var r,e,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,r=C.find_among_b(h,5))switch(C.bra=C.cursor,r){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(j,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(e=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-e,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,r=C.find_among_b(k,6))switch(C.bra=C.cursor,r){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(z,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var d,_,w,b=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],p=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],g=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],h=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],k=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],v=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(r){C.setCurrent(r)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var e=C.cursor;return r(),C.cursor=e,o(),C.limit_backward=e,C.cursor=C.limit,f(),C.cursor=C.limit_backward,s(),!0}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.nl.stemmer,"stemmer-nl"),r.nl.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.nl.stopWordFilter,"stopWordFilter-nl")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.no.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.no.min.js new file mode 100755 index 00000000..92bc7e4e --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.no.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Norwegian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.pt.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.pt.min.js new file mode 100755 index 00000000..6c16996d --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.pt.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Portuguese` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(k,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("a~");continue;case 2:z.slice_from("o~");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function n(){if(z.out_grouping(y,97,250)){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!0;z.cursor++}return!1}return!0}function i(){if(z.in_grouping(y,97,250))for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return g=z.cursor,!0}function o(){var e,r,s=z.cursor;if(z.in_grouping(y,97,250))if(e=z.cursor,n()){if(z.cursor=e,i())return}else g=z.cursor;if(z.cursor=s,z.out_grouping(y,97,250)){if(r=z.cursor,n()){if(z.cursor=r,!z.in_grouping(y,97,250)||z.cursor>=z.limit)return;z.cursor++}g=z.cursor}}function t(){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return!0}function a(){var e=z.cursor;g=z.limit,b=g,h=g,o(),z.cursor=e,t()&&(b=z.cursor,t()&&(h=z.cursor))}function u(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(q,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("ã");continue;case 2:z.slice_from("õ");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function w(){return g<=z.cursor}function m(){return b<=z.cursor}function c(){return h<=z.cursor}function l(){var e;if(z.ket=z.cursor,!(e=z.find_among_b(F,45)))return!1;switch(z.bra=z.cursor,e){case 1:if(!c())return!1;z.slice_del();break;case 2:if(!c())return!1;z.slice_from("log");break;case 3:if(!c())return!1;z.slice_from("u");break;case 4:if(!c())return!1;z.slice_from("ente");break;case 5:if(!m())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(j,4),e&&(z.bra=z.cursor,c()&&(z.slice_del(),1==e&&(z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del()))));break;case 6:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(C,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 7:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(P,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 8:if(!c())return!1;z.slice_del(),z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del());break;case 9:if(!w()||!z.eq_s_b(1,"e"))return!1;z.slice_from("ir")}return!0}function f(){var e,r;if(z.cursor>=g){if(r=z.limit_backward,z.limit_backward=g,z.ket=z.cursor,e=z.find_among_b(S,120))return z.bra=z.cursor,1==e&&z.slice_del(),z.limit_backward=r,!0;z.limit_backward=r}return!1}function d(){var e;z.ket=z.cursor,(e=z.find_among_b(W,7))&&(z.bra=z.cursor,1==e&&w()&&z.slice_del())}function v(e,r){if(z.eq_s_b(1,e)){z.bra=z.cursor;var s=z.limit-z.cursor;if(z.eq_s_b(1,r))return z.cursor=z.limit-s,w()&&z.slice_del(),!1}return!0}function p(){var e;if(z.ket=z.cursor,e=z.find_among_b(L,4))switch(z.bra=z.cursor,e){case 1:w()&&(z.slice_del(),z.ket=z.cursor,z.limit-z.cursor,v("u","g")&&v("i","c"));break;case 2:z.slice_from("c")}}function _(){if(!l()&&(z.cursor=z.limit,!f()))return z.cursor=z.limit,void d();z.cursor=z.limit,z.ket=z.cursor,z.eq_s_b(1,"i")&&(z.bra=z.cursor,z.eq_s_b(1,"c")&&(z.cursor=z.limit,w()&&z.slice_del()))}var h,b,g,k=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],q=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],j=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],C=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],P=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],F=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],S=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],W=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],L=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],y=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],z=new s;this.setCurrent=function(e){z.setCurrent(e)},this.getCurrent=function(){return z.getCurrent()},this.stem=function(){var r=z.cursor;return e(),z.cursor=r,a(),z.limit_backward=r,z.cursor=z.limit,_(),z.cursor=z.limit,p(),z.cursor=z.limit_backward,u(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.ro.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.ro.min.js new file mode 100755 index 00000000..72771401 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.ro.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Romanian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=function(){var i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){function e(e,i){L.eq_s(1,e)&&(L.ket=L.cursor,L.in_grouping(W,97,259)&&L.slice_from(i))}function n(){for(var i,r;;){if(i=L.cursor,L.in_grouping(W,97,259)&&(r=L.cursor,L.bra=r,e("u","U"),L.cursor=r,e("i","I")),L.cursor=i,L.cursor>=L.limit)break;L.cursor++}}function t(){if(L.out_grouping(W,97,259)){for(;!L.in_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}return!0}function a(){if(L.in_grouping(W,97,259))for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}function o(){var e,i,r=L.cursor;if(L.in_grouping(W,97,259)){if(e=L.cursor,!t())return void(h=L.cursor);if(L.cursor=e,!a())return void(h=L.cursor)}L.cursor=r,L.out_grouping(W,97,259)&&(i=L.cursor,t()&&(L.cursor=i,L.in_grouping(W,97,259)&&L.cursor=L.limit)return!1;L.cursor++}for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!1;L.cursor++}return!0}function c(){var e=L.cursor;h=L.limit,k=h,g=h,o(),L.cursor=e,u()&&(k=L.cursor,u()&&(g=L.cursor))}function s(){for(var e;;){if(L.bra=L.cursor,e=L.find_among(z,3))switch(L.ket=L.cursor,e){case 1:L.slice_from("i");continue;case 2:L.slice_from("u");continue;case 3:if(L.cursor>=L.limit)break;L.cursor++;continue}break}}function w(){return h<=L.cursor}function m(){return k<=L.cursor}function l(){return g<=L.cursor}function f(){var e,i;if(L.ket=L.cursor,(e=L.find_among_b(C,16))&&(L.bra=L.cursor,m()))switch(e){case 1:L.slice_del();break;case 2:L.slice_from("a");break;case 3:L.slice_from("e");break;case 4:L.slice_from("i");break;case 5:i=L.limit-L.cursor,L.eq_s_b(2,"ab")||(L.cursor=L.limit-i,L.slice_from("i"));break;case 6:L.slice_from("at");break;case 7:L.slice_from("aţi")}}function p(){var e,i=L.limit-L.cursor;if(L.ket=L.cursor,(e=L.find_among_b(P,46))&&(L.bra=L.cursor,m())){switch(e){case 1:L.slice_from("abil");break;case 2:L.slice_from("ibil");break;case 3:L.slice_from("iv");break;case 4:L.slice_from("ic");break;case 5:L.slice_from("at");break;case 6:L.slice_from("it")}return _=!0,L.cursor=L.limit-i,!0}return!1}function d(){var e,i;for(_=!1;;)if(i=L.limit-L.cursor,!p()){L.cursor=L.limit-i;break}if(L.ket=L.cursor,(e=L.find_among_b(F,62))&&(L.bra=L.cursor,l())){switch(e){case 1:L.slice_del();break;case 2:L.eq_s_b(1,"ţ")&&(L.bra=L.cursor,L.slice_from("t"));break;case 3:L.slice_from("ist")}_=!0}}function b(){var e,i,r;if(L.cursor>=h){if(i=L.limit_backward,L.limit_backward=h,L.ket=L.cursor,e=L.find_among_b(q,94))switch(L.bra=L.cursor,e){case 1:if(r=L.limit-L.cursor,!L.out_grouping_b(W,97,259)&&(L.cursor=L.limit-r,!L.eq_s_b(1,"u")))break;case 2:L.slice_del()}L.limit_backward=i}}function v(){var e;L.ket=L.cursor,(e=L.find_among_b(S,5))&&(L.bra=L.cursor,w()&&1==e&&L.slice_del())}var _,g,k,h,z=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],C=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],P=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],F=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],q=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],S=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var e=L.cursor;return n(),L.cursor=e,c(),L.limit_backward=e,L.cursor=L.limit,f(),L.cursor=L.limit,d(),L.cursor=L.limit,_||(L.cursor=L.limit,b(),L.cursor=L.limit),v(),L.cursor=L.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.ru.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.ru.min.js new file mode 100755 index 00000000..186cc485 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.ru.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Russian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){function e(){for(;!W.in_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function t(){for(;!W.out_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function w(){b=W.limit,_=b,e()&&(b=W.cursor,t()&&e()&&t()&&(_=W.cursor))}function i(){return _<=W.cursor}function u(e,n){var r,t;if(W.ket=W.cursor,r=W.find_among_b(e,n)){switch(W.bra=W.cursor,r){case 1:if(t=W.limit-W.cursor,!W.eq_s_b(1,"а")&&(W.cursor=W.limit-t,!W.eq_s_b(1,"я")))return!1;case 2:W.slice_del()}return!0}return!1}function o(){return u(h,9)}function s(e,n){var r;return W.ket=W.cursor,!!(r=W.find_among_b(e,n))&&(W.bra=W.cursor,1==r&&W.slice_del(),!0)}function c(){return s(g,26)}function m(){return!!c()&&(u(C,8),!0)}function f(){return s(k,2)}function l(){return u(P,46)}function a(){s(v,36)}function p(){var e;W.ket=W.cursor,(e=W.find_among_b(F,2))&&(W.bra=W.cursor,i()&&1==e&&W.slice_del())}function d(){var e;if(W.ket=W.cursor,e=W.find_among_b(q,4))switch(W.bra=W.cursor,e){case 1:if(W.slice_del(),W.ket=W.cursor,!W.eq_s_b(1,"н"))break;W.bra=W.cursor;case 2:if(!W.eq_s_b(1,"н"))break;case 3:W.slice_del()}}var _,b,h=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],g=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],C=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],k=[new n("сь",-1,1),new n("ся",-1,1)],P=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],v=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],F=[new n("ост",-1,1),new n("ость",-1,1)],q=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],S=[33,65,8,232],W=new r;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){return w(),W.cursor=W.limit,!(W.cursor=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.sv.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.sv.min.js new file mode 100755 index 00000000..3e5eb640 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.sv.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Swedish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.ta.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.ta.min.js new file mode 100755 index 00000000..a644bed2 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.ta.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="஀-உஊ-ஏஐ-ஙச-ட஠-னப-யர-ஹ஺-ிீ-௉ொ-௏ௐ-௙௚-௟௠-௩௪-௯௰-௹௺-௿a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.te.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.te.min.js new file mode 100755 index 00000000..9fa7a93b --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.te.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.te=function(){this.pipeline.reset(),this.pipeline.add(e.te.trimmer,e.te.stopWordFilter,e.te.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.te.stemmer))},e.te.wordCharacters="ఀ-ఄఅ-ఔక-హా-ౌౕ-ౖౘ-ౚౠ-ౡౢ-ౣ౦-౯౸-౿఼ఽ్ౝ౷౤౥",e.te.trimmer=e.trimmerSupport.generateTrimmer(e.te.wordCharacters),e.Pipeline.registerFunction(e.te.trimmer,"trimmer-te"),e.te.stopWordFilter=e.generateStopWordFilter("అందరూ అందుబాటులో అడగండి అడగడం అడ్డంగా అనుగుణంగా అనుమతించు అనుమతిస్తుంది అయితే ఇప్పటికే ఉన్నారు ఎక్కడైనా ఎప్పుడు ఎవరైనా ఎవరో ఏ ఏదైనా ఏమైనప్పటికి ఒక ఒకరు కనిపిస్తాయి కాదు కూడా గా గురించి చుట్టూ చేయగలిగింది తగిన తర్వాత దాదాపు దూరంగా నిజంగా పై ప్రకారం ప్రక్కన మధ్య మరియు మరొక మళ్ళీ మాత్రమే మెచ్చుకో వద్ద వెంట వేరుగా వ్యతిరేకంగా సంబంధం".split(" ")),e.te.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.te.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.te.stemmer,"stemmer-te"),e.Pipeline.registerFunction(e.te.stopWordFilter,"stopWordFilter-te")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.th.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.th.min.js new file mode 100755 index 00000000..dee3aac6 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.th.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[฀-๿]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");var t=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t):t});var n=i.toString().replace(/^\s+/,"");return t.cut(n).split("|")}}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.tr.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.tr.min.js new file mode 100755 index 00000000..563f6ec1 --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.tr.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Turkish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=function(){var i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){function r(r,i,e){for(;;){var n=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(r,i,e)){Dr.cursor=Dr.limit-n;break}if(Dr.cursor=Dr.limit-n,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function n(){var i,e;i=Dr.limit-Dr.cursor,r(Wr,97,305);for(var n=0;nDr.limit_backward&&(Dr.cursor--,e=Dr.limit-Dr.cursor,i()))?(Dr.cursor=Dr.limit-e,!0):(Dr.cursor=Dr.limit-n,r()?(Dr.cursor=Dr.limit-n,!1):(Dr.cursor=Dr.limit-n,!(Dr.cursor<=Dr.limit_backward)&&(Dr.cursor--,!!i()&&(Dr.cursor=Dr.limit-n,!0))))}function u(r){return t(r,function(){return Dr.in_grouping_b(Wr,97,305)})}function o(){return u(function(){return Dr.eq_s_b(1,"n")})}function s(){return u(function(){return Dr.eq_s_b(1,"s")})}function c(){return u(function(){return Dr.eq_s_b(1,"y")})}function l(){return t(function(){return Dr.in_grouping_b(Lr,105,305)},function(){return Dr.out_grouping_b(Wr,97,305)})}function a(){return Dr.find_among_b(ur,10)&&l()}function m(){return n()&&Dr.in_grouping_b(Lr,105,305)&&s()}function d(){return Dr.find_among_b(or,2)}function f(){return n()&&Dr.in_grouping_b(Lr,105,305)&&c()}function b(){return n()&&Dr.find_among_b(sr,4)}function w(){return n()&&Dr.find_among_b(cr,4)&&o()}function _(){return n()&&Dr.find_among_b(lr,2)&&c()}function k(){return n()&&Dr.find_among_b(ar,2)}function p(){return n()&&Dr.find_among_b(mr,4)}function g(){return n()&&Dr.find_among_b(dr,2)}function y(){return n()&&Dr.find_among_b(fr,4)}function z(){return n()&&Dr.find_among_b(br,2)}function v(){return n()&&Dr.find_among_b(wr,2)&&c()}function h(){return Dr.eq_s_b(2,"ki")}function q(){return n()&&Dr.find_among_b(_r,2)&&o()}function C(){return n()&&Dr.find_among_b(kr,4)&&c()}function P(){return n()&&Dr.find_among_b(pr,4)}function F(){return n()&&Dr.find_among_b(gr,4)&&c()}function S(){return Dr.find_among_b(yr,4)}function W(){return n()&&Dr.find_among_b(zr,2)}function L(){return n()&&Dr.find_among_b(vr,4)}function x(){return n()&&Dr.find_among_b(hr,8)}function A(){return Dr.find_among_b(qr,2)}function E(){return n()&&Dr.find_among_b(Cr,32)&&c()}function j(){return Dr.find_among_b(Pr,8)&&c()}function T(){return n()&&Dr.find_among_b(Fr,4)&&c()}function Z(){return Dr.eq_s_b(3,"ken")&&c()}function B(){var r=Dr.limit-Dr.cursor;return!(T()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,Z()))))}function D(){if(A()){var r=Dr.limit-Dr.cursor;if(S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T())return!1}return!0}function G(){if(W()){Dr.bra=Dr.cursor,Dr.slice_del();var r=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,x()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,T()||(Dr.cursor=Dr.limit-r)))),nr=!1,!1}return!0}function H(){if(!L())return!0;var r=Dr.limit-Dr.cursor;return!E()&&(Dr.cursor=Dr.limit-r,!j())}function I(){var r,i=Dr.limit-Dr.cursor;return!(S()||(Dr.cursor=Dr.limit-i,F()||(Dr.cursor=Dr.limit-i,P()||(Dr.cursor=Dr.limit-i,C()))))||(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,T()||(Dr.cursor=Dr.limit-r),!1)}function J(){var r,i=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,nr=!0,B()&&(Dr.cursor=Dr.limit-i,D()&&(Dr.cursor=Dr.limit-i,G()&&(Dr.cursor=Dr.limit-i,H()&&(Dr.cursor=Dr.limit-i,I()))))){if(Dr.cursor=Dr.limit-i,!x())return;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T()||(Dr.cursor=Dr.limit-r)}Dr.bra=Dr.cursor,Dr.slice_del()}function K(){var r,i,e,n;if(Dr.ket=Dr.cursor,h()){if(r=Dr.limit-Dr.cursor,p())return Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,a()&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))),!0;if(Dr.cursor=Dr.limit-r,w()){if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,e=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-e,!m()&&(Dr.cursor=Dr.limit-e,!K())))return!0;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}return!0}if(Dr.cursor=Dr.limit-r,g()){if(n=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-n,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-n,!K())return!1;return!0}}return!1}function M(r){if(Dr.ket=Dr.cursor,!g()&&(Dr.cursor=Dr.limit-r,!k()))return!1;var i=Dr.limit-Dr.cursor;if(d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-i,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-i,!K())return!1;return!0}function N(r){if(Dr.ket=Dr.cursor,!z()&&(Dr.cursor=Dr.limit-r,!b()))return!1;var i=Dr.limit-Dr.cursor;return!(!m()&&(Dr.cursor=Dr.limit-i,!d()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)}function O(){var r,i=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,!(!w()&&(Dr.cursor=Dr.limit-i,!v()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,!(!W()||(Dr.bra=Dr.cursor,Dr.slice_del(),!K()))||(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!(a()||(Dr.cursor=Dr.limit-r,m()||(Dr.cursor=Dr.limit-r,K())))||(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)))}function Q(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,!p()&&(Dr.cursor=Dr.limit-e,!f()&&(Dr.cursor=Dr.limit-e,!_())))return!1;if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,a())Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()||(Dr.cursor=Dr.limit-i);else if(Dr.cursor=Dr.limit-r,!W())return!0;return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,K(),!0}function R(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,W())return Dr.bra=Dr.cursor,Dr.slice_del(),void K();if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,q())if(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-r,!m())){if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!W())return;if(Dr.bra=Dr.cursor,Dr.slice_del(),!K())return}Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}else if(Dr.cursor=Dr.limit-e,!M(e)&&(Dr.cursor=Dr.limit-e,!N(e))){if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,y())return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,i=Dr.limit-Dr.cursor,void(a()?(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())):(Dr.cursor=Dr.limit-i,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,K())));if(Dr.cursor=Dr.limit-e,!O()){if(Dr.cursor=Dr.limit-e,d())return Dr.bra=Dr.cursor,void Dr.slice_del();Dr.cursor=Dr.limit-e,K()||(Dr.cursor=Dr.limit-e,Q()||(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,(a()||(Dr.cursor=Dr.limit-e,m()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))))}}}function U(){var r;if(Dr.ket=Dr.cursor,r=Dr.find_among_b(Sr,4))switch(Dr.bra=Dr.cursor,r){case 1:Dr.slice_from("p");break;case 2:Dr.slice_from("ç");break;case 3:Dr.slice_from("t");break;case 4:Dr.slice_from("k")}}function V(){for(;;){var r=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(Wr,97,305)){Dr.cursor=Dr.limit-r;break}if(Dr.cursor=Dr.limit-r,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function X(r,i,e){if(Dr.cursor=Dr.limit-r,V()){var n=Dr.limit-Dr.cursor;if(!Dr.eq_s_b(1,i)&&(Dr.cursor=Dr.limit-n,!Dr.eq_s_b(1,e)))return!0;Dr.cursor=Dr.limit-r;var t=Dr.cursor;return Dr.insert(Dr.cursor,Dr.cursor,e),Dr.cursor=t,!1}return!0}function Y(){var r=Dr.limit-Dr.cursor;(Dr.eq_s_b(1,"d")||(Dr.cursor=Dr.limit-r,Dr.eq_s_b(1,"g")))&&X(r,"a","ı")&&X(r,"e","i")&&X(r,"o","u")&&X(r,"ö","ü")}function $(){for(var r,i=Dr.cursor,e=2;;){for(r=Dr.cursor;!Dr.in_grouping(Wr,97,305);){if(Dr.cursor>=Dr.limit)return Dr.cursor=r,!(e>0)&&(Dr.cursor=i,!0);Dr.cursor++}e--}}function rr(r,i,e){for(;!Dr.eq_s(i,e);){if(Dr.cursor>=Dr.limit)return!0;Dr.cursor++}return(tr=i)!=Dr.limit||(Dr.cursor=r,!1)}function ir(){var r=Dr.cursor;return!rr(r,2,"ad")||(Dr.cursor=r,!rr(r,5,"soyad"))}function er(){var r=Dr.cursor;return!ir()&&(Dr.limit_backward=r,Dr.cursor=Dr.limit,Y(),Dr.cursor=Dr.limit,U(),!0)}var nr,tr,ur=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],or=[new i("leri",-1,-1),new i("ları",-1,-1)],sr=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],cr=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],lr=[new i("a",-1,-1),new i("e",-1,-1)],ar=[new i("na",-1,-1),new i("ne",-1,-1)],mr=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],dr=[new i("nda",-1,-1),new i("nde",-1,-1)],fr=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],br=[new i("ndan",-1,-1),new i("nden",-1,-1)],wr=[new i("la",-1,-1),new i("le",-1,-1)],_r=[new i("ca",-1,-1),new i("ce",-1,-1)],kr=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],pr=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],gr=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],yr=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],zr=[new i("lar",-1,-1),new i("ler",-1,-1)],vr=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],hr=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],qr=[new i("casına",-1,-1),new i("cesine",-1,-1)],Cr=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],Pr=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],Fr=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],Sr=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],Wr=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],Lr=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],xr=[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],Ar=[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],Er=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],jr=[17],Tr=[65],Zr=[65],Br=[["a",xr,97,305],["e",Ar,101,252],["ı",Er,97,305],["i",jr,101,105],["o",Tr,111,117],["ö",Zr,246,252],["u",Tr,111,117]],Dr=new e;this.setCurrent=function(r){Dr.setCurrent(r)},this.getCurrent=function(){return Dr.getCurrent()},this.stem=function(){return!!($()&&(Dr.limit_backward=Dr.cursor,Dr.cursor=Dr.limit,J(),Dr.cursor=Dr.limit,nr&&(R(),Dr.cursor=Dr.limit_backward,er())))}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.vi.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.vi.min.js new file mode 100755 index 00000000..22aed28c --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.vi.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/min/lunr.zh.min.js b/6.2.X/assets/javascripts/lunr/min/lunr.zh.min.js new file mode 100755 index 00000000..fda66e9c --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/min/lunr.zh.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r(require("@node-rs/jieba")):r()(e.lunr)}(this,function(e){return function(r,t){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==r.version[0];r.zh=function(){this.pipeline.reset(),this.pipeline.add(r.zh.trimmer,r.zh.stopWordFilter,r.zh.stemmer),i?this.tokenizer=r.zh.tokenizer:(r.tokenizer&&(r.tokenizer=r.zh.tokenizer),this.tokenizerFn&&(this.tokenizerFn=r.zh.tokenizer))},r.zh.tokenizer=function(n){if(!arguments.length||null==n||void 0==n)return[];if(Array.isArray(n))return n.map(function(e){return i?new r.Token(e.toLowerCase()):e.toLowerCase()});t&&e.load(t);var o=n.toString().trim().toLowerCase(),s=[];e.cut(o,!0).forEach(function(e){s=s.concat(e.split(" "))}),s=s.filter(function(e){return!!e});var u=0;return s.map(function(e,t){if(i){var n=o.indexOf(e,u),s={};return s.position=[n,e.length],s.index=t,u=n,new r.Token(e,s)}return e})},r.zh.wordCharacters="\\w一-龥",r.zh.trimmer=r.trimmerSupport.generateTrimmer(r.zh.wordCharacters),r.Pipeline.registerFunction(r.zh.trimmer,"trimmer-zh"),r.zh.stemmer=function(){return function(e){return e}}(),r.Pipeline.registerFunction(r.zh.stemmer,"stemmer-zh"),r.zh.stopWordFilter=r.generateStopWordFilter("的 一 不 在 人 有 是 为 為 以 于 於 上 他 而 后 後 之 来 來 及 了 因 下 可 到 由 这 這 与 與 也 此 但 并 並 个 個 其 已 无 無 小 我 们 們 起 最 再 今 去 好 只 又 或 很 亦 某 把 那 你 乃 它 吧 被 比 别 趁 当 當 从 從 得 打 凡 儿 兒 尔 爾 该 該 各 给 給 跟 和 何 还 還 即 几 幾 既 看 据 據 距 靠 啦 另 么 麽 每 嘛 拿 哪 您 凭 憑 且 却 卻 让 讓 仍 啥 如 若 使 谁 誰 虽 雖 随 隨 同 所 她 哇 嗡 往 些 向 沿 哟 喲 用 咱 则 則 怎 曾 至 致 着 著 诸 諸 自".split(" ")),r.Pipeline.registerFunction(r.zh.stopWordFilter,"stopWordFilter-zh")}}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/tinyseg.js b/6.2.X/assets/javascripts/lunr/tinyseg.js new file mode 100755 index 00000000..167fa6dd --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/tinyseg.js @@ -0,0 +1,206 @@ +/** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ +;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + + return function(lunr) { + // TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript + // (c) 2008 Taku Kudo + // TinySegmenter is freely distributable under the terms of a new BSD licence. + // For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt + + function TinySegmenter() { + var patterns = { + "[一二三四五六七八九十百千万億兆]":"M", + "[一-龠々〆ヵヶ]":"H", + "[ぁ-ん]":"I", + "[ァ-ヴーア-ン゙ー]":"K", + "[a-zA-Za-zA-Z]":"A", + "[0-90-9]":"N" + } + this.chartype_ = []; + for (var i in patterns) { + var regexp = new RegExp(i); + this.chartype_.push([regexp, patterns[i]]); + } + + this.BIAS__ = -332 + this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378}; + this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920}; + this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266}; + this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352}; + this.BP2__ = {"BO":60,"OO":-1762}; + this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965}; + this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146}; + this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699}; + this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973}; + this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682}; + this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669}; + this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990}; + this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832}; + this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649}; + this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393}; + this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841}; + this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68}; + this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591}; + this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685}; + this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156}; + this.TW1__ = {"につい":-4681,"東京都":2026}; + this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216}; + this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287}; + this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865}; + this.UC1__ = {"A":484,"K":93,"M":645,"O":-505}; + this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646}; + this.UC3__ = {"A":-1370,"I":2311}; + this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646}; + this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831}; + this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387}; + this.UP1__ = {"O":-214}; + this.UP2__ = {"B":69,"O":935}; + this.UP3__ = {"B":189}; + this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422}; + this.UQ2__ = {"BH":216,"BI":113,"OK":1759}; + this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212}; + this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135}; + this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568}; + this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278}; + this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637}; + this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343}; + this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496}; + + return this; + } + TinySegmenter.prototype.ctype_ = function(str) { + for (var i in this.chartype_) { + if (str.match(this.chartype_[i][0])) { + return this.chartype_[i][1]; + } + } + return "O"; + } + + TinySegmenter.prototype.ts_ = function(v) { + if (v) { return v; } + return 0; + } + + TinySegmenter.prototype.segment = function(input) { + if (input == null || input == undefined || input == "") { + return []; + } + var result = []; + var seg = ["B3","B2","B1"]; + var ctype = ["O","O","O"]; + var o = input.split(""); + for (i = 0; i < o.length; ++i) { + seg.push(o[i]); + ctype.push(this.ctype_(o[i])) + } + seg.push("E1"); + seg.push("E2"); + seg.push("E3"); + ctype.push("O"); + ctype.push("O"); + ctype.push("O"); + var word = seg[3]; + var p1 = "U"; + var p2 = "U"; + var p3 = "U"; + for (var i = 4; i < seg.length - 3; ++i) { + var score = this.BIAS__; + var w1 = seg[i-3]; + var w2 = seg[i-2]; + var w3 = seg[i-1]; + var w4 = seg[i]; + var w5 = seg[i+1]; + var w6 = seg[i+2]; + var c1 = ctype[i-3]; + var c2 = ctype[i-2]; + var c3 = ctype[i-1]; + var c4 = ctype[i]; + var c5 = ctype[i+1]; + var c6 = ctype[i+2]; + score += this.ts_(this.UP1__[p1]); + score += this.ts_(this.UP2__[p2]); + score += this.ts_(this.UP3__[p3]); + score += this.ts_(this.BP1__[p1 + p2]); + score += this.ts_(this.BP2__[p2 + p3]); + score += this.ts_(this.UW1__[w1]); + score += this.ts_(this.UW2__[w2]); + score += this.ts_(this.UW3__[w3]); + score += this.ts_(this.UW4__[w4]); + score += this.ts_(this.UW5__[w5]); + score += this.ts_(this.UW6__[w6]); + score += this.ts_(this.BW1__[w2 + w3]); + score += this.ts_(this.BW2__[w3 + w4]); + score += this.ts_(this.BW3__[w4 + w5]); + score += this.ts_(this.TW1__[w1 + w2 + w3]); + score += this.ts_(this.TW2__[w2 + w3 + w4]); + score += this.ts_(this.TW3__[w3 + w4 + w5]); + score += this.ts_(this.TW4__[w4 + w5 + w6]); + score += this.ts_(this.UC1__[c1]); + score += this.ts_(this.UC2__[c2]); + score += this.ts_(this.UC3__[c3]); + score += this.ts_(this.UC4__[c4]); + score += this.ts_(this.UC5__[c5]); + score += this.ts_(this.UC6__[c6]); + score += this.ts_(this.BC1__[c2 + c3]); + score += this.ts_(this.BC2__[c3 + c4]); + score += this.ts_(this.BC3__[c4 + c5]); + score += this.ts_(this.TC1__[c1 + c2 + c3]); + score += this.ts_(this.TC2__[c2 + c3 + c4]); + score += this.ts_(this.TC3__[c3 + c4 + c5]); + score += this.ts_(this.TC4__[c4 + c5 + c6]); + // score += this.ts_(this.TC5__[c4 + c5 + c6]); + score += this.ts_(this.UQ1__[p1 + c1]); + score += this.ts_(this.UQ2__[p2 + c2]); + score += this.ts_(this.UQ3__[p3 + c3]); + score += this.ts_(this.BQ1__[p2 + c2 + c3]); + score += this.ts_(this.BQ2__[p2 + c3 + c4]); + score += this.ts_(this.BQ3__[p3 + c2 + c3]); + score += this.ts_(this.BQ4__[p3 + c3 + c4]); + score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]); + score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]); + score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]); + score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]); + var p = "O"; + if (score > 0) { + result.push(word); + word = ""; + p = "B"; + } + p1 = p2; + p2 = p3; + p3 = p; + word += seg[i]; + } + result.push(word); + + return result; + } + + lunr.TinySegmenter = TinySegmenter; + }; + +})); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/lunr/wordcut.js b/6.2.X/assets/javascripts/lunr/wordcut.js new file mode 100755 index 00000000..0d898c9e --- /dev/null +++ b/6.2.X/assets/javascripts/lunr/wordcut.js @@ -0,0 +1,6708 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.lunr || (g.lunr = {})).wordcut = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1; + }) + this.addWords(words, false) + } + if(finalize){ + this.finalizeDict(); + } + }, + + dictSeek: function (l, r, ch, strOffset, pos) { + var ans = null; + while (l <= r) { + var m = Math.floor((l + r) / 2), + dict_item = this.dict[m], + len = dict_item.length; + if (len <= strOffset) { + l = m + 1; + } else { + var ch_ = dict_item[strOffset]; + if (ch_ < ch) { + l = m + 1; + } else if (ch_ > ch) { + r = m - 1; + } else { + ans = m; + if (pos == LEFT) { + r = m - 1; + } else { + l = m + 1; + } + } + } + } + return ans; + }, + + isFinal: function (acceptor) { + return this.dict[acceptor.l].length == acceptor.strOffset; + }, + + createAcceptor: function () { + return { + l: 0, + r: this.dict.length - 1, + strOffset: 0, + isFinal: false, + dict: this, + transit: function (ch) { + return this.dict.transit(this, ch); + }, + isError: false, + tag: "DICT", + w: 1, + type: "DICT" + }; + }, + + transit: function (acceptor, ch) { + var l = this.dictSeek(acceptor.l, + acceptor.r, + ch, + acceptor.strOffset, + LEFT); + if (l !== null) { + var r = this.dictSeek(l, + acceptor.r, + ch, + acceptor.strOffset, + RIGHT); + acceptor.l = l; + acceptor.r = r; + acceptor.strOffset++; + acceptor.isFinal = this.isFinal(acceptor); + } else { + acceptor.isError = true; + } + return acceptor; + }, + + sortuniq: function(a){ + return a.sort().filter(function(item, pos, arr){ + return !pos || item != arr[pos - 1]; + }) + }, + + flatten: function(a){ + //[[1,2],[3]] -> [1,2,3] + return [].concat.apply([], a); + } +}; +module.exports = WordcutDict; + +}).call(this,"/dist/tmp") +},{"glob":16,"path":22}],3:[function(require,module,exports){ +var WordRule = { + createAcceptor: function(tag) { + if (tag["WORD_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + var lch = ch.toLowerCase(); + if (lch >= "a" && lch <= "z") { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "WORD_RULE", + type: "WORD_RULE", + w: 1}; + } +}; + +var NumberRule = { + createAcceptor: function(tag) { + if (tag["NUMBER_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (ch >= "0" && ch <= "9") { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "NUMBER_RULE", + type: "NUMBER_RULE", + w: 1}; + } +}; + +var SpaceRule = { + tag: "SPACE_RULE", + createAcceptor: function(tag) { + + if (tag["SPACE_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (ch == " " || ch == "\t" || ch == "\r" || ch == "\n" || + ch == "\u00A0" || ch=="\u2003"//nbsp and emsp + ) { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: SpaceRule.tag, + w: 1, + type: "SPACE_RULE"}; + } +} + +var SingleSymbolRule = { + tag: "SINSYM", + createAcceptor: function(tag) { + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (this.strOffset == 0 && ch.match(/^[\@\(\)\/\,\-\."`]$/)) { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "SINSYM", + w: 1, + type: "SINSYM"}; + } +} + + +var LatinRules = [WordRule, SpaceRule, SingleSymbolRule, NumberRule]; + +module.exports = LatinRules; + +},{}],4:[function(require,module,exports){ +var _ = require("underscore") + , WordcutCore = require("./wordcut_core"); +var PathInfoBuilder = { + + /* + buildByPartAcceptors: function(path, acceptors, i) { + var + var genInfos = partAcceptors.reduce(function(genInfos, acceptor) { + + }, []); + + return genInfos; + } + */ + + buildByAcceptors: function(path, finalAcceptors, i) { + var self = this; + var infos = finalAcceptors.map(function(acceptor) { + var p = i - acceptor.strOffset + 1 + , _info = path[p]; + + var info = {p: p, + mw: _info.mw + (acceptor.mw === undefined ? 0 : acceptor.mw), + w: acceptor.w + _info.w, + unk: (acceptor.unk ? acceptor.unk : 0) + _info.unk, + type: acceptor.type}; + + if (acceptor.type == "PART") { + for(var j = p + 1; j <= i; j++) { + path[j].merge = p; + } + info.merge = p; + } + + return info; + }); + return infos.filter(function(info) { return info; }); + }, + + fallback: function(path, leftBoundary, text, i) { + var _info = path[leftBoundary]; + if (text[i].match(/[\u0E48-\u0E4E]/)) { + if (leftBoundary != 0) + leftBoundary = path[leftBoundary].p; + return {p: leftBoundary, + mw: 0, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; +/* } else if(leftBoundary > 0 && path[leftBoundary].type !== "UNK") { + leftBoundary = path[leftBoundary].p; + return {p: leftBoundary, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; */ + } else { + return {p: leftBoundary, + mw: _info.mw, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; + } + }, + + build: function(path, finalAcceptors, i, leftBoundary, text) { + var basicPathInfos = this.buildByAcceptors(path, finalAcceptors, i); + if (basicPathInfos.length > 0) { + return basicPathInfos; + } else { + return [this.fallback(path, leftBoundary, text, i)]; + } + } +}; + +module.exports = function() { + return _.clone(PathInfoBuilder); +} + +},{"./wordcut_core":8,"underscore":25}],5:[function(require,module,exports){ +var _ = require("underscore"); + + +var PathSelector = { + selectPath: function(paths) { + var path = paths.reduce(function(selectedPath, path) { + if (selectedPath == null) { + return path; + } else { + if (path.unk < selectedPath.unk) + return path; + if (path.unk == selectedPath.unk) { + if (path.mw < selectedPath.mw) + return path + if (path.mw == selectedPath.mw) { + if (path.w < selectedPath.w) + return path; + } + } + return selectedPath; + } + }, null); + return path; + }, + + createPath: function() { + return [{p:null, w:0, unk:0, type: "INIT", mw:0}]; + } +}; + +module.exports = function() { + return _.clone(PathSelector); +}; + +},{"underscore":25}],6:[function(require,module,exports){ +function isMatch(pat, offset, ch) { + if (pat.length <= offset) + return false; + var _ch = pat[offset]; + return _ch == ch || + (_ch.match(/[กข]/) && ch.match(/[ก-ฮ]/)) || + (_ch.match(/[มบ]/) && ch.match(/[ก-ฮ]/)) || + (_ch.match(/\u0E49/) && ch.match(/[\u0E48-\u0E4B]/)); +} + +var Rule0 = { + pat: "เหก็ม", + createAcceptor: function(tag) { + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (isMatch(Rule0.pat, this.strOffset,ch)) { + this.isFinal = (this.strOffset + 1 == Rule0.pat.length); + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "THAI_RULE", + type: "THAI_RULE", + w: 1}; + } +}; + +var PartRule = { + createAcceptor: function(tag) { + return {strOffset: 0, + patterns: [ + "แก", "เก", "ก้", "กก์", "กา", "กี", "กิ", "กืก" + ], + isFinal: false, + transit: function(ch) { + var offset = this.strOffset; + this.patterns = this.patterns.filter(function(pat) { + return isMatch(pat, offset, ch); + }); + + if (this.patterns.length > 0) { + var len = 1 + offset; + this.isFinal = this.patterns.some(function(pat) { + return pat.length == len; + }); + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "PART", + type: "PART", + unk: 1, + w: 1}; + } +}; + +var ThaiRules = [Rule0, PartRule]; + +module.exports = ThaiRules; + +},{}],7:[function(require,module,exports){ +var sys = require("sys") + , WordcutDict = require("./dict") + , WordcutCore = require("./wordcut_core") + , PathInfoBuilder = require("./path_info_builder") + , PathSelector = require("./path_selector") + , Acceptors = require("./acceptors") + , latinRules = require("./latin_rules") + , thaiRules = require("./thai_rules") + , _ = require("underscore"); + + +var Wordcut = Object.create(WordcutCore); +Wordcut.defaultPathInfoBuilder = PathInfoBuilder; +Wordcut.defaultPathSelector = PathSelector; +Wordcut.defaultAcceptors = Acceptors; +Wordcut.defaultLatinRules = latinRules; +Wordcut.defaultThaiRules = thaiRules; +Wordcut.defaultDict = WordcutDict; + + +Wordcut.initNoDict = function(dict_path) { + var self = this; + self.pathInfoBuilder = new self.defaultPathInfoBuilder; + self.pathSelector = new self.defaultPathSelector; + self.acceptors = new self.defaultAcceptors; + self.defaultLatinRules.forEach(function(rule) { + self.acceptors.creators.push(rule); + }); + self.defaultThaiRules.forEach(function(rule) { + self.acceptors.creators.push(rule); + }); +}; + +Wordcut.init = function(dict_path, withDefault, additionalWords) { + withDefault = withDefault || false; + this.initNoDict(); + var dict = _.clone(this.defaultDict); + dict.init(dict_path, withDefault, additionalWords); + this.acceptors.creators.push(dict); +}; + +module.exports = Wordcut; + +},{"./acceptors":1,"./dict":2,"./latin_rules":3,"./path_info_builder":4,"./path_selector":5,"./thai_rules":6,"./wordcut_core":8,"sys":28,"underscore":25}],8:[function(require,module,exports){ +var WordcutCore = { + + buildPath: function(text) { + var self = this + , path = self.pathSelector.createPath() + , leftBoundary = 0; + self.acceptors.reset(); + for (var i = 0; i < text.length; i++) { + var ch = text[i]; + self.acceptors.transit(ch); + + var possiblePathInfos = self + .pathInfoBuilder + .build(path, + self.acceptors.getFinalAcceptors(), + i, + leftBoundary, + text); + var selectedPath = self.pathSelector.selectPath(possiblePathInfos) + + path.push(selectedPath); + if (selectedPath.type !== "UNK") { + leftBoundary = i; + } + } + return path; + }, + + pathToRanges: function(path) { + var e = path.length - 1 + , ranges = []; + + while (e > 0) { + var info = path[e] + , s = info.p; + + if (info.merge !== undefined && ranges.length > 0) { + var r = ranges[ranges.length - 1]; + r.s = info.merge; + s = r.s; + } else { + ranges.push({s:s, e:e}); + } + e = s; + } + return ranges.reverse(); + }, + + rangesToText: function(text, ranges, delimiter) { + return ranges.map(function(r) { + return text.substring(r.s, r.e); + }).join(delimiter); + }, + + cut: function(text, delimiter) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + return this + .rangesToText(text, ranges, + (delimiter === undefined ? "|" : delimiter)); + }, + + cutIntoRanges: function(text, noText) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + + if (!noText) { + ranges.forEach(function(r) { + r.text = text.substring(r.s, r.e); + }); + } + return ranges; + }, + + cutIntoArray: function(text) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + + return ranges.map(function(r) { + return text.substring(r.s, r.e) + }); + } +}; + +module.exports = WordcutCore; + +},{}],9:[function(require,module,exports){ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// when used in node, this will actually load the util module we depend on +// versus loading the builtin util module as happens otherwise +// this is a bug in node module loading as far as I am concerned +var util = require('util/'); + +var pSlice = Array.prototype.slice; +var hasOwn = Object.prototype.hasOwnProperty; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } + else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = stackStartFunction.name; + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && !isFinite(value)) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) { + return a === b; + } + var aIsArgs = isArguments(a), + bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + var ka = objectKeys(a), + kb = objectKeys(b), + key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +},{"util/":28}],10:[function(require,module,exports){ +'use strict'; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} + +},{}],11:[function(require,module,exports){ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + + +},{"balanced-match":10,"concat-map":13}],12:[function(require,module,exports){ + +},{}],13:[function(require,module,exports){ +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; + +},{}],14:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],15:[function(require,module,exports){ +(function (process){ +exports.alphasort = alphasort +exports.alphasorti = alphasorti +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored + +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) +} + +var path = require("path") +var minimatch = require("minimatch") +var isAbsolute = require("path-is-absolute") +var Minimatch = minimatch.Minimatch + +function alphasorti (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()) +} + +function alphasort (a, b) { + return a.localeCompare(b) +} + +function setupIgnores (self, options) { + self.ignore = options.ignore || [] + + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] + + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} + +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern) + } + + return { + matcher: new Minimatch(pattern), + gmatcher: gmatcher + } +} + +function setopts (self, pattern, options) { + if (!options) + options = {} + + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } + + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) + + setupIgnores(self, options) + + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = options.cwd + self.changedCwd = path.resolve(options.cwd) !== cwd + } + + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") + + self.nomount = !!options.nomount + + // disable comments and negation unless the user explicitly + // passes in false as the option. + options.nonegate = options.nonegate === false ? false : true + options.nocomment = options.nocomment === false ? false : true + deprecationWarning(options) + + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options +} + +// TODO(isaacs): remove entirely in v6 +// exported to reset in tests +exports.deprecationWarned +function deprecationWarning(options) { + if (!options.nonegate || !options.nocomment) { + if (process.noDeprecation !== true && !exports.deprecationWarned) { + var msg = 'glob WARNING: comments and negation will be disabled in v6' + if (process.throwDeprecation) + throw new Error(msg) + else if (process.traceDeprecation) + console.trace(msg) + else + console.error(msg) + + exports.deprecationWarned = true + } + } +} + +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) + + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } + + if (!nou) + all = Object.keys(all) + + if (!self.nosort) + all = all.sort(self.nocase ? alphasorti : alphasort) + + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + return !(/\/$/.test(e)) + }) + } + } + + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) + + self.found = all +} + +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' + + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) + + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } + + return m +} + +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } + return abs +} + + +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +}).call(this,require('_process')) +},{"_process":24,"minimatch":20,"path":22,"path-is-absolute":23}],16:[function(require,module,exports){ +(function (process){ +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. + +module.exports = glob + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var inherits = require('inherits') +var EE = require('events').EventEmitter +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var globSync = require('./sync.js') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = require('inflight') +var util = require('util') +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored + +var once = require('once') + +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} + + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) + } + + return new Glob(pattern, options, cb) +} + +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync + +// old api surface +glob.glob = glob + +glob.hasMagic = function (pattern, options_) { + var options = util._extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true + } + + return false +} + +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) + } + + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + + setopts(this, pattern, options) + this._didRealPath = false + + // process each pattern in the minimatch set + var n = this.minimatch.set.length + + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) + } + + var self = this + var n = this.minimatch.set.length + this._processing = 0 + this.matches = new Array(n) + + this._emitQueue = [] + this._processQueue = [] + this.paused = false + + if (this.noprocess) + return this + + if (n === 0) + return done() + + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + + function done () { + --self._processing + if (self._processing <= 0) + self._finish() + } +} + +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return + + if (this.realpath && !this._didRealpath) + return this._realpath() + + common.finish(this) + this.emit('end', this.found) +} + +Glob.prototype._realpath = function () { + if (this._didRealpath) + return + + this._didRealpath = true + + var n = this.matches.length + if (n === 0) + return this._finish() + + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) + + function next () { + if (--n === 0) + self._finish() + } +} + +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() + + var found = Object.keys(matchset) + var self = this + var n = found.length + + if (n === 0) + return cb() + + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + fs.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here + + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) +} + +Glob.prototype._mark = function (p) { + return common.mark(this, p) +} + +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} + +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') + } +} + +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } + } +} + +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') + + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return + } + + //console.error('PROCESS %d', this._processing, pattern) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +} + +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} + +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return + + if (this.matches[index][e]) + return + + if (isIgnored(this, e)) + return + + if (this.paused) { + this._emitQueue.push([index, e]) + return + } + + var abs = this._makeAbs(e) + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } + + if (this.mark) + e = this._mark(e) + + this.matches[index][e] = true + + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) +} + +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return + + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) + + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) + + if (lstatcb) + fs.lstat(abs, lstatcb) + + function lstatcb_ (er, lstat) { + if (er) + return cb() + + var isSym = lstat.isSymbolicLink() + self.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } +} + +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return + + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() + + if (Array.isArray(c)) + return cb(null, c) + } + + var self = this + fs.readdir(abs, readdirCb(this, abs, cb)) +} + +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) + } +} + +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + return cb(null, entries) +} + +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return + + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } + + return cb() +} + +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) + + var isSym = this.symlinks[abs] + var len = entries.length + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) + } + + cb() +} + +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + + //console.error('ps2', prefix, exists) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) + cb() +} + +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return cb() + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) + + if (needDir && c === 'FILE') + return cb() + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) + } + } + + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) + } + } +} + +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er) { + this.statCache[abs] = false + return cb() + } + + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat + + if (abs.slice(-1) === '/' && !stat.isDirectory()) + return cb(null, false, stat) + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return cb() + + return cb(null, c, stat) +} + +}).call(this,require('_process')) +},{"./common.js":15,"./sync.js":17,"_process":24,"assert":9,"events":14,"fs":12,"inflight":18,"inherits":19,"minimatch":20,"once":21,"path":22,"path-is-absolute":23,"util":28}],17:[function(require,module,exports){ +(function (process){ +module.exports = globSync +globSync.GlobSync = GlobSync + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var Glob = require('./glob.js').Glob +var util = require('util') +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored + +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + return new GlobSync(pattern, options).found +} + +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') + + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) + + setopts(this, pattern, options) + + if (this.noprocess) + return this + + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} + +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = fs.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) + } + common.finish(this) +} + + +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} + + +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this.matches[index][e] = true + } + // This was the last one, and no stats were needed + return + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} + + +GlobSync.prototype._emitMatch = function (index, e) { + var abs = this._makeAbs(e) + if (this.mark) + e = this._mark(e) + + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[this._makeAbs(e)] + if (c === 'DIR' || Array.isArray(c)) + return + } + + this.matches[index][e] = true + if (this.stat) + this._stat(e) +} + + +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) + + var entries + var lstat + var stat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + // lstat failed, doesn't exist + return null + } + + var isSym = lstat.isSymbolicLink() + this.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) + + return entries +} + +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries + + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null + + if (Array.isArray(c)) + return c + } + + try { + return this._readdirEntries(abs, fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} + +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + + // mark and cache dir-ness + return entries +} + +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} + +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { + + var entries = this._readdir(abs, inGlobStar) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) + + var len = entries.length + var isSym = this.symlinks[abs] + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) + } +} + +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this.matches[index][prefix] = true +} + +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return false + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + return false + } + + if (lstat.isSymbolicLink()) { + try { + stat = fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } + } + + this.statCache[abs] = stat + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return false + + return c +} + +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +}).call(this,require('_process')) +},{"./common.js":15,"./glob.js":16,"_process":24,"assert":9,"fs":12,"minimatch":20,"path":22,"path-is-absolute":23,"util":28}],18:[function(require,module,exports){ +(function (process){ +var wrappy = require('wrappy') +var reqs = Object.create(null) +var once = require('once') + +module.exports = wrappy(inflight) + +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null + } else { + reqs[key] = [cb] + return makeres(key) + } +} + +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) + + // XXX It's somewhat ambiguous whether a new callback added in this + // pass should be queued for later execution if something in the + // list of callbacks throws, or if it should just be discarded. + // However, it's such an edge case that it hardly matters, and either + // choice is likely as surprising as the other. + // As it happens, we do go ahead and schedule it for later execution. + try { + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + } finally { + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + } + }) +} + +function slice (args) { + var length = args.length + var array = [] + + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} + +}).call(this,require('_process')) +},{"_process":24,"once":21,"wrappy":29}],19:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],20:[function(require,module,exports){ +module.exports = minimatch +minimatch.Minimatch = Minimatch + +var path = { sep: '/' } +try { + path = require('path') +} catch (er) {} + +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = require('brace-expansion') + +var plTypes = { + '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, + '?': { open: '(?:', close: ')?' }, + '+': { open: '(?:', close: ')+' }, + '*': { open: '(?:', close: ')*' }, + '@': { open: '(?:', close: ')' } +} + +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' + +// * => any number of characters +var star = qmark + '*?' + +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') + +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) +} + +// normalizes slashes. +var slashSplit = /\/+/ + +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) + } +} + +function ext (a, b) { + a = a || {} + b = b || {} + var t = {} + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + return t +} + +minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return minimatch + + var orig = minimatch + + var m = function minimatch (p, pattern, options) { + return orig.minimatch(p, pattern, ext(def, options)) + } + + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } + + return m +} + +Minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return Minimatch + return minimatch.defaults(def).Minimatch +} + +function minimatch (p, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false + } + + // "" only matches "" + if (pattern.trim() === '') return p === '' + + return new Minimatch(pattern, options).match(p) +} + +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) + } + + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + pattern = pattern.trim() + + // windows support: need to use /, not \ + if (path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } + + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false + + // make the set of regexps etc. + this.make() +} + +Minimatch.prototype.debug = function () {} + +Minimatch.prototype.make = make +function make () { + // don't do it more than once. + if (this._made) return + + var pattern = this.pattern + var options = this.options + + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return + } + + // step 1: figure out negation, etc. + this.parseNegate() + + // step 2: expand braces + var set = this.globSet = this.braceExpand() + + if (options.debug) this.debug = console.error + + this.debug(this.pattern, set) + + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) + + this.debug(this.pattern, set) + + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) + + this.debug(this.pattern, set) + + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) + + this.debug(this.pattern, set) + + this.set = set +} + +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 + + if (options.nonegate) return + + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} + +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} + +Minimatch.prototype.braceExpand = braceExpand + +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } + + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern + + if (typeof pattern === 'undefined') { + throw new TypeError('undefined pattern') + } + + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } + + return expand(pattern) +} + +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + if (pattern.length > 1024 * 64) { + throw new TypeError('pattern is too long') + } + + var options = this.options + + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' + + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this + + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } + + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) + + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } + + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false + + case '\\': + clearStateChar() + escaping = true + continue + + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + + case '(': + if (inClass) { + re += '(' + continue + } + + if (!stateChar) { + re += '\\(' + continue + } + + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } + + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue + + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + + clearStateChar() + re += '|' + continue + + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() + + if (inClass) { + re += '\\' + c + continue + } + + inClass = true + classStart = i + reClassStart = re.length + re += c + continue + + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } + + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } + + // finish up the class. + hasMagic = true + inClass = false + re += c + continue + + default: + // swallow any state char that wasn't consumed + clearStateChar() + + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } + + re += c + + } // switch + } // for + + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } + + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } + + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) + + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type + + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } + + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } + + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } + + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] + + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) + + nlLast += nlAfter + + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter + + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } + + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } + + if (addPatternStart) { + re = patternStart + re + } + + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } + + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } + + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') + } + + regExp._glob = pattern + regExp._src = re + + return regExp +} + +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} + +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp + + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' + + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' + + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' + + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false + } + return this.regexp +} + +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} + +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' + + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } + + // should be unreachable. + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} + +},{"brace-expansion":11,"path":22}],21:[function(require,module,exports){ +var wrappy = require('wrappy') +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + +},{"wrappy":29}],22:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +}).call(this,require('_process')) +},{"_process":24}],23:[function(require,module,exports){ +(function (process){ +'use strict'; + +function posix(path) { + return path.charAt(0) === '/'; +} + +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); +} + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; + +}).call(this,require('_process')) +},{"_process":24}],24:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],25:[function(require,module,exports){ +// Underscore.js 1.8.3 +// http://underscorejs.org +// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind, + nativeCreate = Object.create; + + // Naked function reference for surrogate-prototype-swapping. + var Ctor = function(){}; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.8.3'; + + // Internal function that returns an efficient (for current engines) version + // of the passed-in callback, to be repeatedly applied in other Underscore + // functions. + var optimizeCb = function(func, context, argCount) { + if (context === void 0) return func; + switch (argCount == null ? 3 : argCount) { + case 1: return function(value) { + return func.call(context, value); + }; + case 2: return function(value, other) { + return func.call(context, value, other); + }; + case 3: return function(value, index, collection) { + return func.call(context, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(context, accumulator, value, index, collection); + }; + } + return function() { + return func.apply(context, arguments); + }; + }; + + // A mostly-internal function to generate callbacks that can be applied + // to each element in a collection, returning the desired result — either + // identity, an arbitrary callback, a property matcher, or a property accessor. + var cb = function(value, context, argCount) { + if (value == null) return _.identity; + if (_.isFunction(value)) return optimizeCb(value, context, argCount); + if (_.isObject(value)) return _.matcher(value); + return _.property(value); + }; + _.iteratee = function(value, context) { + return cb(value, context, Infinity); + }; + + // An internal function for creating assigner functions. + var createAssigner = function(keysFunc, undefinedOnly) { + return function(obj) { + var length = arguments.length; + if (length < 2 || obj == null) return obj; + for (var index = 1; index < length; index++) { + var source = arguments[index], + keys = keysFunc(source), + l = keys.length; + for (var i = 0; i < l; i++) { + var key = keys[i]; + if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; + } + } + return obj; + }; + }; + + // An internal function for creating a new object that inherits from another. + var baseCreate = function(prototype) { + if (!_.isObject(prototype)) return {}; + if (nativeCreate) return nativeCreate(prototype); + Ctor.prototype = prototype; + var result = new Ctor; + Ctor.prototype = null; + return result; + }; + + var property = function(key) { + return function(obj) { + return obj == null ? void 0 : obj[key]; + }; + }; + + // Helper for collection methods to determine whether a collection + // should be iterated as an array or as an object + // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength + // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 + var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; + var getLength = property('length'); + var isArrayLike = function(collection) { + var length = getLength(collection); + return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; + }; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles raw objects in addition to array-likes. Treats all + // sparse array-likes as if they were dense. + _.each = _.forEach = function(obj, iteratee, context) { + iteratee = optimizeCb(iteratee, context); + var i, length; + if (isArrayLike(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + iteratee(obj[i], i, obj); + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + iteratee(obj[keys[i]], keys[i], obj); + } + } + return obj; + }; + + // Return the results of applying the iteratee to each element. + _.map = _.collect = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + results = Array(length); + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + results[index] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Create a reducing function iterating left or right. + function createReduce(dir) { + // Optimized iterator function as using arguments.length + // in the main function will deoptimize the, see #1991. + function iterator(obj, iteratee, memo, keys, index, length) { + for (; index >= 0 && index < length; index += dir) { + var currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + } + + return function(obj, iteratee, memo, context) { + iteratee = optimizeCb(iteratee, context, 4); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + index = dir > 0 ? 0 : length - 1; + // Determine the initial value if none is provided. + if (arguments.length < 3) { + memo = obj[keys ? keys[index] : index]; + index += dir; + } + return iterator(obj, iteratee, memo, keys, index, length); + }; + } + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. + _.reduce = _.foldl = _.inject = createReduce(1); + + // The right-associative version of reduce, also known as `foldr`. + _.reduceRight = _.foldr = createReduce(-1); + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var key; + if (isArrayLike(obj)) { + key = _.findIndex(obj, predicate, context); + } else { + key = _.findKey(obj, predicate, context); + } + if (key !== void 0 && key !== -1) return obj[key]; + }; + + // Return all the elements that pass a truth test. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + predicate = cb(predicate, context); + _.each(obj, function(value, index, list) { + if (predicate(value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, _.negate(cb(predicate)), context); + }; + + // Determine whether all of the elements match a truth test. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (!predicate(obj[currentKey], currentKey, obj)) return false; + } + return true; + }; + + // Determine if at least one element in the object matches a truth test. + // Aliased as `any`. + _.some = _.any = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (predicate(obj[currentKey], currentKey, obj)) return true; + } + return false; + }; + + // Determine if the array or object contains a given item (using `===`). + // Aliased as `includes` and `include`. + _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { + if (!isArrayLike(obj)) obj = _.values(obj); + if (typeof fromIndex != 'number' || guard) fromIndex = 0; + return _.indexOf(obj, item, fromIndex) >= 0; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + var func = isFunc ? method : value[method]; + return func == null ? func : func.apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matcher(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matcher(attrs)); + }; + + // Return the maximum element (or element-based computation). + _.max = function(obj, iteratee, context) { + var result = -Infinity, lastComputed = -Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value > result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed > lastComputed || computed === -Infinity && result === -Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iteratee, context) { + var result = Infinity, lastComputed = Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value < result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed < lastComputed || computed === Infinity && result === Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Shuffle a collection, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var set = isArrayLike(obj) ? obj : _.values(obj); + var length = set.length; + var shuffled = Array(length); + for (var index = 0, rand; index < length; index++) { + rand = _.random(0, index); + if (rand !== index) shuffled[index] = shuffled[rand]; + shuffled[rand] = set[index]; + } + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (!isArrayLike(obj)) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // Sort the object's values by a criterion produced by an iteratee. + _.sortBy = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iteratee(value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iteratee, context) { + var result = {}; + iteratee = cb(iteratee, context); + _.each(obj, function(value, index) { + var key = iteratee(value, index, obj); + behavior(result, value, key); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, value, key) { + if (_.has(result, key)) result[key].push(value); else result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, value, key) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, value, key) { + if (_.has(result, key)) result[key]++; else result[key] = 1; + }); + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (isArrayLike(obj)) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return isArrayLike(obj) ? obj.length : _.keys(obj).length; + }; + + // Split a collection into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(obj, predicate, context) { + predicate = cb(predicate, context); + var pass = [], fail = []; + _.each(obj, function(value, key, obj) { + (predicate(value, key, obj) ? pass : fail).push(value); + }); + return [pass, fail]; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[0]; + return _.initial(array, array.length - n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. + _.initial = function(array, n, guard) { + return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[array.length - 1]; + return _.rest(array, Math.max(0, array.length - n)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, n == null || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, strict, startIndex) { + var output = [], idx = 0; + for (var i = startIndex || 0, length = getLength(input); i < length; i++) { + var value = input[i]; + if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { + //flatten current level of array or arguments object + if (!shallow) value = flatten(value, shallow, strict); + var j = 0, len = value.length; + output.length += len; + while (j < len) { + output[idx++] = value[j++]; + } + } else if (!strict) { + output[idx++] = value; + } + } + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, false); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iteratee, context) { + if (!_.isBoolean(isSorted)) { + context = iteratee; + iteratee = isSorted; + isSorted = false; + } + if (iteratee != null) iteratee = cb(iteratee, context); + var result = []; + var seen = []; + for (var i = 0, length = getLength(array); i < length; i++) { + var value = array[i], + computed = iteratee ? iteratee(value, i, array) : value; + if (isSorted) { + if (!i || seen !== computed) result.push(value); + seen = computed; + } else if (iteratee) { + if (!_.contains(seen, computed)) { + seen.push(computed); + result.push(value); + } + } else if (!_.contains(result, value)) { + result.push(value); + } + } + return result; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(flatten(arguments, true, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var result = []; + var argsLength = arguments.length; + for (var i = 0, length = getLength(array); i < length; i++) { + var item = array[i]; + if (_.contains(result, item)) continue; + for (var j = 1; j < argsLength; j++) { + if (!_.contains(arguments[j], item)) break; + } + if (j === argsLength) result.push(item); + } + return result; + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = flatten(arguments, true, true, 1); + return _.filter(array, function(value){ + return !_.contains(rest, value); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + return _.unzip(arguments); + }; + + // Complement of _.zip. Unzip accepts an array of arrays and groups + // each array's elements on shared indices + _.unzip = function(array) { + var length = array && _.max(array, getLength).length || 0; + var result = Array(length); + + for (var index = 0; index < length; index++) { + result[index] = _.pluck(array, index); + } + return result; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, length = getLength(list); i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // Generator function to create the findIndex and findLastIndex functions + function createPredicateIndexFinder(dir) { + return function(array, predicate, context) { + predicate = cb(predicate, context); + var length = getLength(array); + var index = dir > 0 ? 0 : length - 1; + for (; index >= 0 && index < length; index += dir) { + if (predicate(array[index], index, array)) return index; + } + return -1; + }; + } + + // Returns the first index on an array-like that passes a predicate test + _.findIndex = createPredicateIndexFinder(1); + _.findLastIndex = createPredicateIndexFinder(-1); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iteratee, context) { + iteratee = cb(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = getLength(array); + while (low < high) { + var mid = Math.floor((low + high) / 2); + if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; + } + return low; + }; + + // Generator function to create the indexOf and lastIndexOf functions + function createIndexFinder(dir, predicateFind, sortedIndex) { + return function(array, item, idx) { + var i = 0, length = getLength(array); + if (typeof idx == 'number') { + if (dir > 0) { + i = idx >= 0 ? idx : Math.max(idx + length, i); + } else { + length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; + } + } else if (sortedIndex && idx && length) { + idx = sortedIndex(array, item); + return array[idx] === item ? idx : -1; + } + if (item !== item) { + idx = predicateFind(slice.call(array, i, length), _.isNaN); + return idx >= 0 ? idx + i : -1; + } + for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { + if (array[idx] === item) return idx; + } + return -1; + }; + } + + // Return the position of the first occurrence of an item in an array, + // or -1 if the item is not included in the array. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); + _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (stop == null) { + stop = start || 0; + start = 0; + } + step = step || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = Array(length); + + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Determines whether to execute a function as a constructor + // or a normal function with the provided arguments + var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { + if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); + var self = baseCreate(sourceFunc.prototype); + var result = sourceFunc.apply(self, args); + if (_.isObject(result)) return result; + return self; + }; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); + var args = slice.call(arguments, 2); + var bound = function() { + return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); + }; + return bound; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + var bound = function() { + var position = 0, length = boundArgs.length; + var args = Array(length); + for (var i = 0; i < length; i++) { + args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; + } + while (position < arguments.length) args.push(arguments[position++]); + return executeBound(func, bound, this, this, args); + }; + return bound; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var i, length = arguments.length, key; + if (length <= 1) throw new Error('bindAll must be passed function names'); + for (i = 1; i < length; i++) { + key = arguments[i]; + obj[key] = _.bind(obj[key], obj); + } + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memoize = function(key) { + var cache = memoize.cache; + var address = '' + (hasher ? hasher.apply(this, arguments) : key); + if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); + return cache[address]; + }; + memoize.cache = {}; + return memoize; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ + return func.apply(null, args); + }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = _.partial(_.delay, _, 1); + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) options = {}; + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + previous = now; + result = func.apply(context, args); + if (!timeout) context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a negated version of the passed-in predicate. + _.negate = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var args = arguments; + var start = args.length - 1; + return function() { + var i = start; + var result = args[start].apply(this, arguments); + while (i--) result = args[i].call(this, result); + return result; + }; + }; + + // Returns a function that will only be executed on and after the Nth call. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Returns a function that will only be executed up to (but not including) the Nth call. + _.before = function(times, func) { + var memo; + return function() { + if (--times > 0) { + memo = func.apply(this, arguments); + } + if (times <= 1) func = null; + return memo; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = _.partial(_.before, 2); + + // Object Functions + // ---------------- + + // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. + var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); + var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', + 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; + + function collectNonEnumProps(obj, keys) { + var nonEnumIdx = nonEnumerableProps.length; + var constructor = obj.constructor; + var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; + + // Constructor is a special case. + var prop = 'constructor'; + if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); + + while (nonEnumIdx--) { + prop = nonEnumerableProps[nonEnumIdx]; + if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { + keys.push(prop); + } + } + } + + // Retrieve the names of an object's own properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); + return keys; + }; + + // Retrieve all the property names of an object. + _.allKeys = function(obj) { + if (!_.isObject(obj)) return []; + var keys = []; + for (var key in obj) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Returns the results of applying the iteratee to each element of the object + // In contrast to _.map it returns an object + _.mapObject = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = _.keys(obj), + length = keys.length, + results = {}, + currentKey; + for (var index = 0; index < length; index++) { + currentKey = keys[index]; + results[currentKey] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = createAssigner(_.allKeys); + + // Assigns a given object with all the own properties in the passed-in object(s) + // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) + _.extendOwn = _.assign = createAssigner(_.keys); + + // Returns the first key on an object that passes a predicate test + _.findKey = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = _.keys(obj), key; + for (var i = 0, length = keys.length; i < length; i++) { + key = keys[i]; + if (predicate(obj[key], key, obj)) return key; + } + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(object, oiteratee, context) { + var result = {}, obj = object, iteratee, keys; + if (obj == null) return result; + if (_.isFunction(oiteratee)) { + keys = _.allKeys(obj); + iteratee = optimizeCb(oiteratee, context); + } else { + keys = flatten(arguments, false, false, 1); + iteratee = function(value, key, obj) { return key in obj; }; + obj = Object(obj); + } + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i]; + var value = obj[key]; + if (iteratee(value, key, obj)) result[key] = value; + } + return result; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj, iteratee, context) { + if (_.isFunction(iteratee)) { + iteratee = _.negate(iteratee); + } else { + var keys = _.map(flatten(arguments, false, false, 1), String); + iteratee = function(value, key) { + return !_.contains(keys, key); + }; + } + return _.pick(obj, iteratee, context); + }; + + // Fill in a given object with default properties. + _.defaults = createAssigner(_.allKeys, true); + + // Creates an object that inherits from the given prototype object. + // If additional properties are provided then they will be added to the + // created object. + _.create = function(prototype, props) { + var result = baseCreate(prototype); + if (props) _.extendOwn(result, props); + return result; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Returns whether an object has a given set of `key:value` pairs. + _.isMatch = function(object, attrs) { + var keys = _.keys(attrs), length = keys.length; + if (object == null) return !length; + var obj = Object(object); + for (var i = 0; i < length; i++) { + var key = keys[i]; + if (attrs[key] !== obj[key] || !(key in obj)) return false; + } + return true; + }; + + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a === 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className !== toString.call(b)) return false; + switch (className) { + // Strings, numbers, regular expressions, dates, and booleans are compared by value. + case '[object RegExp]': + // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return '' + a === '' + b; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. + // Object(NaN) is equivalent to NaN + if (+a !== +a) return +b !== +b; + // An `egal` comparison is performed for other numeric values. + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a === +b; + } + + var areArrays = className === '[object Array]'; + if (!areArrays) { + if (typeof a != 'object' || typeof b != 'object') return false; + + // Objects with different constructors are not equivalent, but `Object`s or `Array`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && + _.isFunction(bCtor) && bCtor instanceof bCtor) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + + // Initializing stack of traversed objects. + // It's done here since we only need them for objects and arrays comparison. + aStack = aStack || []; + bStack = bStack || []; + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] === a) return bStack[length] === b; + } + + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + + // Recursively compare objects and arrays. + if (areArrays) { + // Compare array lengths to determine if a deep comparison is necessary. + length = a.length; + if (length !== b.length) return false; + // Deep compare the contents, ignoring non-numeric properties. + while (length--) { + if (!eq(a[length], b[length], aStack, bStack)) return false; + } + } else { + // Deep compare objects. + var keys = _.keys(a), key; + length = keys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (_.keys(b).length !== length) return false; + while (length--) { + // Deep compare each member + key = keys[length]; + if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return true; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; + return _.keys(obj).length === 0; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) === '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE < 9), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return _.has(obj, 'callee'); + }; + } + + // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, + // IE 11 (#1621), and in Safari 8 (#1929). + if (typeof /./ != 'function' && typeof Int8Array != 'object') { + _.isFunction = function(obj) { + return typeof obj == 'function' || false; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj !== +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return obj != null && hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iteratees. + _.identity = function(value) { + return value; + }; + + // Predicate-generating functions. Often useful outside of Underscore. + _.constant = function(value) { + return function() { + return value; + }; + }; + + _.noop = function(){}; + + _.property = property; + + // Generates a function for a given object that returns a given property. + _.propertyOf = function(obj) { + return obj == null ? function(){} : function(key) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of + // `key:value` pairs. + _.matcher = _.matches = function(attrs) { + attrs = _.extendOwn({}, attrs); + return function(obj) { + return _.isMatch(obj, attrs); + }; + }; + + // Run a function **n** times. + _.times = function(n, iteratee, context) { + var accum = Array(Math.max(0, n)); + iteratee = optimizeCb(iteratee, context, 1); + for (var i = 0; i < n; i++) accum[i] = iteratee(i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { + return new Date().getTime(); + }; + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + var unescapeMap = _.invert(escapeMap); + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped + var source = '(?:' + _.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + _.escape = createEscaper(escapeMap); + _.unescape = createEscaper(unescapeMap); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property, fallback) { + var value = object == null ? void 0 : object[property]; + if (value === void 0) { + value = fallback; + } + return _.isFunction(value) ? value.call(object) : value; + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\u2028|\u2029/g; + + var escapeChar = function(match) { + return '\\' + escapes[match]; + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + // NB: `oldSettings` only exists for backwards compatibility. + _.template = function(text, settings, oldSettings) { + if (!settings && oldSettings) settings = oldSettings; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset).replace(escaper, escapeChar); + index = offset + match.length; + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } else if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } else if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + + // Adobe VMs need the match returned to produce the correct offest. + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + 'return __p;\n'; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled source as a convenience for precompilation. + var argument = settings.variable || 'obj'; + template.source = 'function(' + argument + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function. Start chaining a wrapped Underscore object. + _.chain = function(obj) { + var instance = _(obj); + instance._chain = true; + return instance; + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(instance, obj) { + return instance._chain ? _(obj).chain() : obj; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + _.each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result(this, func.apply(_, args)); + }; + }); + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; + return result(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + _.each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result(this, method.apply(this._wrapped, arguments)); + }; + }); + + // Extracts the result from a wrapped and chained object. + _.prototype.value = function() { + return this._wrapped; + }; + + // Provide unwrapping proxy for some methods used in engine operations + // such as arithmetic and JSON stringification. + _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; + + _.prototype.toString = function() { + return '' + this._wrapped; + }; + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}.call(this)); + +},{}],26:[function(require,module,exports){ +arguments[4][19][0].apply(exports,arguments) +},{"dup":19}],27:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],28:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":27,"_process":24,"inherits":26}],29:[function(require,module,exports){ +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} + +},{}]},{},[7])(7) +}); \ No newline at end of file diff --git a/6.2.X/assets/javascripts/workers/search.1e90e0fb.min.js b/6.2.X/assets/javascripts/workers/search.1e90e0fb.min.js new file mode 100755 index 00000000..ff43aedd --- /dev/null +++ b/6.2.X/assets/javascripts/workers/search.1e90e0fb.min.js @@ -0,0 +1,2 @@ +"use strict";(()=>{var xe=Object.create;var G=Object.defineProperty,ve=Object.defineProperties,Se=Object.getOwnPropertyDescriptor,Te=Object.getOwnPropertyDescriptors,Qe=Object.getOwnPropertyNames,Y=Object.getOwnPropertySymbols,Ee=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty,be=Object.prototype.propertyIsEnumerable;var Z=Math.pow,J=(t,e,r)=>e in t?G(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,_=(t,e)=>{for(var r in e||(e={}))X.call(e,r)&&J(t,r,e[r]);if(Y)for(var r of Y(e))be.call(e,r)&&J(t,r,e[r]);return t},B=(t,e)=>ve(t,Te(e));var Le=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var we=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Qe(e))!X.call(t,i)&&i!==r&&G(t,i,{get:()=>e[i],enumerable:!(n=Se(e,i))||n.enumerable});return t};var Pe=(t,e,r)=>(r=t!=null?xe(Ee(t)):{},we(e||!t||!t.__esModule?G(r,"default",{value:t,enumerable:!0}):r,t));var W=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var te=Le((K,ee)=>{(function(){var t=function(e){var r=new t.Builder;return r.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),r.searchPipeline.add(t.stemmer),e.call(r,r),r.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(r){e.console&&console.warn&&console.warn(r)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var r=Object.create(null),n=Object.keys(e),i=0;i0){var f=t.utils.clone(r)||{};f.position=[a,c],f.index=s.length,s.push(new t.Token(n.slice(a,o),f))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,r){r in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+r),e.label=r,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var r=e.label&&e.label in this.registeredFunctions;r||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var r=new t.Pipeline;return e.forEach(function(n){var i=t.Pipeline.registeredFunctions[n];if(i)r.add(i);else throw new Error("Cannot load unregistered function: "+n)}),r},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(r){t.Pipeline.warnIfFunctionNotRegistered(r),this._stack.push(r)},this)},t.Pipeline.prototype.after=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");n=n+1,this._stack.splice(n,0,r)},t.Pipeline.prototype.before=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");this._stack.splice(n,0,r)},t.Pipeline.prototype.remove=function(e){var r=this._stack.indexOf(e);r!=-1&&this._stack.splice(r,1)},t.Pipeline.prototype.run=function(e){for(var r=this._stack.length,n=0;n1&&(oe&&(n=s),o!=e);)i=n-r,s=r+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ou?f+=2:a==u&&(r+=n[c+1]*i[f+1],c+=2,f+=2);return r},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),r=1,n=0;r0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}if(s.str.length==0&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var f=s.str.charAt(0),g=s.str.charAt(1),l;g in s.node.edges?l=s.node.edges[g]:(l=new t.TokenSet,s.node.edges[g]=l),s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:f+s.str.slice(2)})}}}return n},t.TokenSet.fromString=function(e){for(var r=new t.TokenSet,n=r,i=0,s=e.length;i=e;r--){var n=this.uncheckedNodes[r],i=n.child.toString();i in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[i]:(n.child._str=i,this.minimizedNodes[i]=n.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(r){var n=new t.QueryParser(e,r);n.parse()})},t.Index.prototype.query=function(e){for(var r=new t.Query(this.fields),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,r){typeof define=="function"&&define.amd?define(r):typeof K=="object"?ee.exports=r():e.lunr=r()}(this,function(){return t})})()});var de=Pe(te());function re(t,e=document){let r=ke(t,e);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${t}" to be present`);return r}function ke(t,e=document){return e.querySelector(t)||void 0}Object.entries||(Object.entries=function(t){let e=[];for(let r of Object.keys(t))e.push([r,t[r]]);return e});Object.values||(Object.values=function(t){let e=[];for(let r of Object.keys(t))e.push(t[r]);return e});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(t,e){typeof t=="object"?(this.scrollLeft=t.left,this.scrollTop=t.top):(this.scrollLeft=t,this.scrollTop=e)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...t){let e=this.parentNode;if(e){t.length===0&&e.removeChild(this);for(let r=t.length-1;r>=0;r--){let n=t[r];typeof n=="string"?n=document.createTextNode(n):n.parentNode&&n.parentNode.removeChild(n),r?e.insertBefore(this.previousSibling,n):e.replaceChild(n,this)}}}));function ne(t){let e=new Map;for(let r of t){let[n]=r.location.split("#"),i=e.get(n);typeof i=="undefined"?e.set(n,r):(e.set(r.location,r),r.parent=i)}return e}function H(t,e,r){var s;e=new RegExp(e,"g");let n,i=0;do{n=e.exec(t);let o=(s=n==null?void 0:n.index)!=null?s:t.length;if(in?e(r,1,n,n=i):t.charAt(i)===">"&&(t.charAt(n+1)==="/"?--s===0&&e(r++,2,n,i+1):t.charAt(i-1)!=="/"&&s++===0&&e(r,0,n,i+1),n=i+1);i>n&&e(r,1,n,i)}function se(t,e,r,n=!1){return q([t],e,r,n).pop()}function q(t,e,r,n=!1){let i=[0];for(let s=1;s>>2&1023,c=a[0]>>>12;i.push(+(u>c)+i[i.length-1])}return t.map((s,o)=>{let a=0,u=new Map;for(let f of r.sort((g,l)=>g-l)){let g=f&1048575,l=f>>>20;if(i[l]!==o)continue;let m=u.get(l);typeof m=="undefined"&&u.set(l,m=[]),m.push(g)}if(u.size===0)return s;let c=[];for(let[f,g]of u){let l=e[f],m=l[0]>>>12,x=l[l.length-1]>>>12,v=l[l.length-1]>>>2&1023;n&&m>a&&c.push(s.slice(a,m));let d=s.slice(m,x+v);for(let y of g.sort((b,E)=>E-b)){let b=(l[y]>>>12)-m,E=(l[y]>>>2&1023)+b;d=[d.slice(0,b),"",d.slice(b,E),"",d.slice(E)].join("")}if(a=x+v,c.push(d)===2)break}return n&&a{var f;switch(i[f=o+=s]||(i[f]=[]),a){case 0:case 2:i[o].push(u<<12|c-u<<2|a);break;case 1:let g=r[n].slice(u,c);H(g,lunr.tokenizer.separator,(l,m)=>{if(typeof lunr.segmenter!="undefined"){let x=g.slice(l,m);if(/^[MHIK]$/.test(lunr.segmenter.ctype_(x))){let v=lunr.segmenter.segment(x);for(let d=0,y=0;dr){return t.trim().split(/"([^"]+)"/g).map((r,n)=>n&1?r.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):r).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").split(/\s+/g).reduce((r,n)=>{let i=e(n);return[...r,...Array.isArray(i)?i:[i]]},[]).map(r=>/([~^]$)/.test(r)?`${r}1`:r).map(r=>/(^[+-]|[~^]\d+$)/.test(r)?r:`${r}*`).join(" ")}function ue(t){return ae(t,e=>{let r=[],n=new lunr.QueryLexer(e);n.run();for(let{type:i,str:s,start:o,end:a}of n.lexemes)switch(i){case"FIELD":["title","text","tags"].includes(s)||(e=[e.slice(0,a)," ",e.slice(a+1)].join(""));break;case"TERM":H(s,lunr.tokenizer.separator,(...u)=>{r.push([e.slice(0,o),s.slice(...u),e.slice(a)].join(""))})}return r})}function ce(t){let e=new lunr.Query(["title","text","tags"]);new lunr.QueryParser(t,e).parse();for(let n of e.clauses)n.usePipeline=!0,n.term.startsWith("*")&&(n.wildcard=lunr.Query.wildcard.LEADING,n.term=n.term.slice(1)),n.term.endsWith("*")&&(n.wildcard=lunr.Query.wildcard.TRAILING,n.term=n.term.slice(0,-1));return e.clauses}function le(t,e){var i;let r=new Set(t),n={};for(let s=0;s0;){let o=i[--s];for(let u=1;un[o]-u&&(r.add(t.slice(o,o+u)),i[s++]=o+u);let a=o+n[o];n[a]&&ar=>{if(typeof r[e]=="undefined")return;let n=[r.location,e].join(":");return t.set(n,lunr.tokenizer.table=[]),r[e]}}function Re(t,e){let[r,n]=[new Set(t),new Set(e)];return[...new Set([...r].filter(i=>!n.has(i)))]}var U=class{constructor({config:e,docs:r,options:n}){let i=Oe(this.table=new Map);this.map=ne(r),this.options=n,this.index=lunr(function(){this.metadataWhitelist=["position"],this.b(0),e.lang.length===1&&e.lang[0]!=="en"?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang)),this.tokenizer=oe,lunr.tokenizer.separator=new RegExp(e.separator),lunr.segmenter="TinySegmenter"in lunr?new lunr.TinySegmenter:void 0;let s=Re(["trimmer","stopWordFilter","stemmer"],e.pipeline);for(let o of e.lang.map(a=>a==="en"?lunr:lunr[a]))for(let a of s)this.pipeline.remove(o[a]),this.searchPipeline.remove(o[a]);this.ref("location");for(let[o,a]of Object.entries(e.fields))this.field(o,B(_({},a),{extractor:i(o)}));for(let o of r)this.add(o,{boost:o.boost})})}search(e){if(e=e.replace(new RegExp("\\p{sc=Han}+","gu"),s=>[...he(s,this.index.invertedIndex)].join("* ")),e=ue(e),!e)return{items:[]};let r=ce(e).filter(s=>s.presence!==lunr.Query.presence.PROHIBITED),n=this.index.search(e).reduce((s,{ref:o,score:a,matchData:u})=>{let c=this.map.get(o);if(typeof c!="undefined"){c=_({},c),c.tags&&(c.tags=[...c.tags]);let f=le(r,Object.keys(u.metadata));for(let l of this.index.fields){if(typeof c[l]=="undefined")continue;let m=[];for(let d of Object.values(u.metadata))typeof d[l]!="undefined"&&m.push(...d[l].position);if(!m.length)continue;let x=this.table.get([c.location,l].join(":")),v=Array.isArray(c[l])?q:se;c[l]=v(c[l],x,m,l!=="text")}let g=+!c.parent+Object.values(f).filter(l=>l).length/Object.keys(f).length;s.push(B(_({},c),{score:a*(1+Z(g,2)),terms:f}))}return s},[]).sort((s,o)=>o.score-s.score).reduce((s,o)=>{let a=this.map.get(o.location);if(typeof a!="undefined"){let u=a.parent?a.parent.location:a.location;s.set(u,[...s.get(u)||[],o])}return s},new Map);for(let[s,o]of n)if(!o.find(a=>a.location===s)){let a=this.map.get(s);o.push(B(_({},a),{score:0,terms:{}}))}let i;if(this.options.suggest){let s=this.index.query(o=>{for(let a of r)o.term(a.term,{fields:["title"],presence:lunr.Query.presence.REQUIRED,wildcard:lunr.Query.wildcard.TRAILING})});i=s.length?Object.keys(s[0].matchData.metadata):[]}return _({items:[...n.values()]},typeof i!="undefined"&&{suggest:i})}};var fe;function Ie(t){return W(this,null,function*(){let e="../lunr";if(typeof parent!="undefined"&&"IFrameWorker"in parent){let n=re("script[src]"),[i]=n.src.split("/worker");e=e.replace("..",i)}let r=[];for(let n of t.lang){switch(n){case"ja":r.push(`${e}/tinyseg.js`);break;case"hi":case"th":r.push(`${e}/wordcut.js`);break}n!=="en"&&r.push(`${e}/min/lunr.${n}.min.js`)}t.lang.length>1&&r.push(`${e}/min/lunr.multi.min.js`),r.length&&(yield importScripts(`${e}/min/lunr.stemmer.support.min.js`,...r))})}function Fe(t){return W(this,null,function*(){switch(t.type){case 0:return yield Ie(t.data.config),fe=new U(t.data),{type:1};case 2:let e=t.data;try{return{type:3,data:fe.search(e)}}catch(r){return console.warn(`Invalid query: ${e} \u2013 see https://bit.ly/2s3ChXG`),console.warn(r),{type:3,data:{items:[]}}}default:throw new TypeError("Invalid message type")}})}self.lunr=de.default;addEventListener("message",t=>W(void 0,null,function*(){postMessage(yield Fe(t.data))}));})(); diff --git a/6.2.X/assets/logo.png b/6.2.X/assets/logo.png new file mode 100755 index 00000000..c02d2b57 Binary files /dev/null and b/6.2.X/assets/logo.png differ diff --git a/6.2.X/assets/stylesheets/glightbox.min.css b/6.2.X/assets/stylesheets/glightbox.min.css new file mode 100755 index 00000000..3c9ff877 --- /dev/null +++ b/6.2.X/assets/stylesheets/glightbox.min.css @@ -0,0 +1 @@ +.glightbox-container{width:100%;height:100%;position:fixed;top:0;left:0;z-index:999999!important;overflow:hidden;-ms-touch-action:none;touch-action:none;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;outline:0}.glightbox-container.inactive{display:none}.glightbox-container .gcontainer{position:relative;width:100%;height:100%;z-index:9999;overflow:hidden}.glightbox-container .gslider{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease;height:100%;left:0;top:0;width:100%;position:relative;overflow:hidden;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.glightbox-container .gslide{width:100%;position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;opacity:0}.glightbox-container .gslide.current{opacity:1;z-index:99999;position:relative}.glightbox-container .gslide.prev{opacity:1;z-index:9999}.glightbox-container .gslide-inner-content{width:100%}.glightbox-container .ginner-container{position:relative;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;max-width:100%;margin:auto;height:100vh}.glightbox-container .ginner-container.gvideo-container{width:100%}.glightbox-container .ginner-container.desc-bottom,.glightbox-container .ginner-container.desc-top{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.glightbox-container .ginner-container.desc-left,.glightbox-container .ginner-container.desc-right{max-width:100%!important}.gslide iframe,.gslide video{outline:0!important;border:none;min-height:165px;-webkit-overflow-scrolling:touch;-ms-touch-action:auto;touch-action:auto}.gslide:not(.current){pointer-events:none}.gslide-image{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.gslide-image img{max-height:100vh;display:block;padding:0;float:none;outline:0;border:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;max-width:100vw;width:auto;height:auto;-o-object-fit:cover;object-fit:cover;-ms-touch-action:none;touch-action:none;margin:auto;min-width:200px}.desc-bottom .gslide-image img,.desc-top .gslide-image img{width:auto}.desc-left .gslide-image img,.desc-right .gslide-image img{width:auto;max-width:100%}.gslide-image img.zoomable{position:relative}.gslide-image img.dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.gslide-video{position:relative;max-width:100vh;width:100%!important}.gslide-video .plyr__poster-enabled.plyr--loading .plyr__poster{display:none}.gslide-video .gvideo-wrapper{width:100%;margin:auto}.gslide-video::before{content:'';position:absolute;width:100%;height:100%;background:rgba(255,0,0,.34);display:none}.gslide-video.playing::before{display:none}.gslide-video.fullscreen{max-width:100%!important;min-width:100%;height:75vh}.gslide-video.fullscreen video{max-width:100%!important;width:100%!important}.gslide-inline{background:#fff;text-align:left;max-height:calc(100vh - 40px);overflow:auto;max-width:100%;margin:auto}.gslide-inline .ginlined-content{padding:20px;width:100%}.gslide-inline .dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.ginlined-content{overflow:auto;display:block!important;opacity:1}.gslide-external{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;min-width:100%;background:#fff;padding:0;overflow:auto;max-height:75vh;height:100%}.gslide-media{display:-webkit-box;display:-ms-flexbox;display:flex;width:auto}.zoomed .gslide-media{-webkit-box-shadow:none!important;box-shadow:none!important}.desc-bottom .gslide-media,.desc-top .gslide-media{margin:0 auto;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gslide-description{position:relative;-webkit-box-flex:1;-ms-flex:1 0 100%;flex:1 0 100%}.gslide-description.description-left,.gslide-description.description-right{max-width:100%}.gslide-description.description-bottom,.gslide-description.description-top{margin:0 auto;width:100%}.gslide-description p{margin-bottom:12px}.gslide-description p:last-child{margin-bottom:0}.zoomed .gslide-description{display:none}.glightbox-button-hidden{display:none}.glightbox-mobile .glightbox-container .gslide-description{height:auto!important;width:100%;position:absolute;bottom:0;padding:19px 11px;max-width:100vw!important;-webkit-box-ordinal-group:3!important;-ms-flex-order:2!important;order:2!important;max-height:78vh;overflow:auto!important;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.75)));background:linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,.75) 100%);-webkit-transition:opacity .3s linear;transition:opacity .3s linear;padding-bottom:50px}.glightbox-mobile .glightbox-container .gslide-title{color:#fff;font-size:1em}.glightbox-mobile .glightbox-container .gslide-desc{color:#a1a1a1}.glightbox-mobile .glightbox-container .gslide-desc a{color:#fff;font-weight:700}.glightbox-mobile .glightbox-container .gslide-desc *{color:inherit}.glightbox-mobile .glightbox-container .gslide-desc .desc-more{color:#fff;opacity:.4}.gdesc-open .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:.4}.gdesc-open .gdesc-inner{padding-bottom:30px}.gdesc-closed .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:1}.greset{-webkit-transition:all .3s ease;transition:all .3s ease}.gabsolute{position:absolute}.grelative{position:relative}.glightbox-desc{display:none!important}.glightbox-open{overflow:hidden}.gloader{height:25px;width:25px;-webkit-animation:lightboxLoader .8s infinite linear;animation:lightboxLoader .8s infinite linear;border:2px solid #fff;border-right-color:transparent;border-radius:50%;position:absolute;display:block;z-index:9999;left:0;right:0;margin:0 auto;top:47%}.goverlay{width:100%;height:calc(100vh + 1px);position:fixed;top:-1px;left:0;background:#000;will-change:opacity}.glightbox-mobile .goverlay{background:#000}.gclose,.gnext,.gprev{z-index:99999;cursor:pointer;width:26px;height:44px;border:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gclose svg,.gnext svg,.gprev svg{display:block;width:25px;height:auto;margin:0;padding:0}.gclose.disabled,.gnext.disabled,.gprev.disabled{opacity:.1}.gclose .garrow,.gnext .garrow,.gprev .garrow{stroke:#fff}.gbtn.focused{outline:2px solid #0f3d81}iframe.wait-autoplay{opacity:0}.glightbox-closing .gclose,.glightbox-closing .gnext,.glightbox-closing .gprev{opacity:0!important}.glightbox-clean .gslide-description{background:#fff}.glightbox-clean .gdesc-inner{padding:22px 20px}.glightbox-clean .gslide-title{font-size:1em;font-weight:400;font-family:arial;color:#000;margin-bottom:19px;line-height:1.4em}.glightbox-clean .gslide-desc{font-size:.86em;margin-bottom:0;font-family:arial;line-height:1.4em}.glightbox-clean .gslide-video{background:#000}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.75);border-radius:4px}.glightbox-clean .gclose path,.glightbox-clean .gnext path,.glightbox-clean .gprev path{fill:#fff}.glightbox-clean .gprev{position:absolute;top:-100%;left:30px;width:40px;height:50px}.glightbox-clean .gnext{position:absolute;top:-100%;right:30px;width:40px;height:50px}.glightbox-clean .gclose{width:35px;height:35px;top:15px;right:10px;position:absolute}.glightbox-clean .gclose svg{width:18px;height:auto}.glightbox-clean .gclose:hover{opacity:1}.gfadeIn{-webkit-animation:gfadeIn .5s ease;animation:gfadeIn .5s ease}.gfadeOut{-webkit-animation:gfadeOut .5s ease;animation:gfadeOut .5s ease}.gslideOutLeft{-webkit-animation:gslideOutLeft .3s ease;animation:gslideOutLeft .3s ease}.gslideInLeft{-webkit-animation:gslideInLeft .3s ease;animation:gslideInLeft .3s ease}.gslideOutRight{-webkit-animation:gslideOutRight .3s ease;animation:gslideOutRight .3s ease}.gslideInRight{-webkit-animation:gslideInRight .3s ease;animation:gslideInRight .3s ease}.gzoomIn{-webkit-animation:gzoomIn .5s ease;animation:gzoomIn .5s ease}.gzoomOut{-webkit-animation:gzoomOut .5s ease;animation:gzoomOut .5s ease}@-webkit-keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes gfadeIn{from{opacity:0}to{opacity:1}}@keyframes gfadeIn{from{opacity:0}to{opacity:1}}@-webkit-keyframes gfadeOut{from{opacity:1}to{opacity:0}}@keyframes gfadeOut{from{opacity:1}to{opacity:0}}@-webkit-keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@-webkit-keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@-webkit-keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@-webkit-keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@media (min-width:769px){.glightbox-container .ginner-container{width:auto;height:auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.glightbox-container .ginner-container.desc-top .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-top .gslide-image,.glightbox-container .ginner-container.desc-top .gslide-image img{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.glightbox-container .ginner-container.desc-left .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-left .gslide-image{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.gslide-image img{max-height:97vh;max-width:100%}.gslide-image img.zoomable{cursor:-webkit-zoom-in;cursor:zoom-in}.zoomed .gslide-image img.zoomable{cursor:-webkit-grab;cursor:grab}.gslide-inline{max-height:95vh}.gslide-external{max-height:100vh}.gslide-description.description-left,.gslide-description.description-right{max-width:275px}.glightbox-open{height:auto}.goverlay{background:rgba(0,0,0,.92)}.glightbox-clean .gslide-media{-webkit-box-shadow:1px 2px 9px 0 rgba(0,0,0,.65);box-shadow:1px 2px 9px 0 rgba(0,0,0,.65)}.glightbox-clean .description-left .gdesc-inner,.glightbox-clean .description-right .gdesc-inner{position:absolute;height:100%;overflow-y:auto}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.32)}.glightbox-clean .gclose:hover,.glightbox-clean .gnext:hover,.glightbox-clean .gprev:hover{background-color:rgba(0,0,0,.7)}.glightbox-clean .gprev{top:45%}.glightbox-clean .gnext{top:45%}}@media (min-width:992px){.glightbox-clean .gclose{opacity:.7;right:20px}}@media screen and (max-height:420px){.goverlay{background:#000}} \ No newline at end of file diff --git a/6.2.X/assets/stylesheets/main.46e89654.min.css b/6.2.X/assets/stylesheets/main.46e89654.min.css new file mode 100755 index 00000000..62142773 --- /dev/null +++ b/6.2.X/assets/stylesheets/main.46e89654.min.css @@ -0,0 +1 @@ +@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-bg-color--light:#f5f5f5b3;--md-code-bg-color--lighter:#f5f5f54d;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset a code{color:var(--md-typeset-a-color)}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none;transition:background-color 125ms}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;transition:color 125ms,background-color 125ms;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{cursor:help;text-decoration:none}.md-typeset [data-preview],.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light)}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}:root{--md-code-select-icon:url('data:image/svg+xml;charset=utf-8,');--md-code-copy-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-code__content{display:grid}.md-code__nav{background-color:var(--md-code-bg-color--lighter);border-radius:.1rem;display:flex;gap:.2rem;padding:.2rem;position:absolute;right:.25em;top:.25em;transition:background-color .25s;z-index:1}:hover>.md-code__nav{background-color:var(--md-code-bg-color--light)}.md-code__button{color:var(--md-default-fg-color--lightest);cursor:pointer;display:block;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;transition:color .25s;width:1.5em}:hover>*>.md-code__button{color:var(--md-default-fg-color--light)}.md-code__button.focus-visible,.md-code__button:hover{color:var(--md-accent-fg-color)}.md-code__button--active{color:var(--md-default-fg-color)!important}.md-code__button:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-code__button[data-md-type=select]:after{-webkit-mask-image:var(--md-code-select-icon);mask-image:var(--md-code-select-icon)}.md-code__button[data-md-type=copy]:after{-webkit-mask-image:var(--md-code-copy-icon);mask-image:var(--md-code-copy-icon)}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed,.md-nav__link--passed code{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}.md-nav__link .md-ellipsis code{word-break:normal}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link .md-typeset{font-size:.7rem;line-height:1.3}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link[for]:focus code,.md-nav__link[for]:hover code,.md-nav__link[href]:focus code,.md-nav__link[href]:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}:root{--md-path-icon:url('data:image/svg+xml;charset=utf-8,')}.md-path{font-size:.7rem;margin:0 .8rem;overflow:auto;padding-top:1.2rem}.md-path:not([hidden]){display:block}@media screen and (min-width:76.25em){.md-path{margin:0 1.2rem}}.md-path__list{align-items:center;display:flex;gap:.2rem;list-style:none;margin:0;padding:0}.md-path__item:not(:first-child){display:inline-flex;gap:.2rem;white-space:nowrap}.md-path__item:not(:first-child):before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline;height:.8rem;-webkit-mask-image:var(--md-path-icon);mask-image:var(--md-path-icon);width:.8rem}.md-path__link{align-items:center;color:var(--md-default-fg-color--light);display:flex}.md-path__link:focus,.md-path__link:hover{color:var(--md-accent-fg-color)}:root{--md-post-pin-icon:url('data:image/svg+xml;charset=utf-8,')}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-pin{background:var(--md-default-fg-color--lightest);border-radius:1rem;margin-top:-.05rem;padding:.2rem}.md-pin:after{background-color:currentcolor;content:"";display:block;height:.6rem;margin:0 auto;-webkit-mask-image:var(--md-post-pin-icon);mask-image:var(--md-post-pin-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.6rem}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-shadow{opacity:.5}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-weight:400;outline:none;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=dialog]>.md-tooltip2__inner{font-size:.64rem;overflow:auto;padding:0 .8rem;pointer-events:auto;width:var(--md-tooltip-width)}[role=dialog]>.md-tooltip2__inner:after,[role=dialog]>.md-tooltip2__inner:before{content:"";display:block;height:.8rem;position:sticky;width:100%;z-index:10}[role=dialog]>.md-tooltip2__inner:before{background:linear-gradient(var(--md-default-bg-color),#0000 75%);top:0}[role=dialog]>.md-tooltip2__inner:after{background:linear-gradient(#0000,var(--md-default-bg-color) 75%);bottom:0}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .linenodiv span[class]{padding-right:.5882352941em}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/6.2.X/assets/stylesheets/palette.ab4e12ef.min.css b/6.2.X/assets/stylesheets/palette.ab4e12ef.min.css new file mode 100755 index 00000000..75aaf842 --- /dev/null +++ b/6.2.X/assets/stylesheets/palette.ab4e12ef.min.css @@ -0,0 +1 @@ +@media screen{[data-md-color-scheme=slate]{--md-default-fg-color:hsla(var(--md-hue),15%,90%,0.82);--md-default-fg-color--light:hsla(var(--md-hue),15%,90%,0.56);--md-default-fg-color--lighter:hsla(var(--md-hue),15%,90%,0.32);--md-default-fg-color--lightest:hsla(var(--md-hue),15%,90%,0.12);--md-default-bg-color:hsla(var(--md-hue),15%,14%,1);--md-default-bg-color--light:hsla(var(--md-hue),15%,14%,0.54);--md-default-bg-color--lighter:hsla(var(--md-hue),15%,14%,0.26);--md-default-bg-color--lightest:hsla(var(--md-hue),15%,14%,0.07);--md-code-fg-color:hsla(var(--md-hue),18%,86%,0.82);--md-code-bg-color:hsla(var(--md-hue),15%,18%,1);--md-code-bg-color--light:hsla(var(--md-hue),15%,18%,0.9);--md-code-bg-color--lighter:hsla(var(--md-hue),15%,18%,0.54);--md-code-hl-color:#2977ff;--md-code-hl-color--light:#2977ff1a;--md-code-hl-number-color:#e6695b;--md-code-hl-special-color:#f06090;--md-code-hl-function-color:#c973d9;--md-code-hl-constant-color:#9383e2;--md-code-hl-keyword-color:#6791e0;--md-code-hl-string-color:#2fb170;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-kbd-color:hsla(var(--md-hue),15%,90%,0.12);--md-typeset-kbd-accent-color:hsla(var(--md-hue),15%,90%,0.2);--md-typeset-kbd-border-color:hsla(var(--md-hue),15%,14%,1);--md-typeset-mark-color:#4287ff4d;--md-typeset-table-color:hsla(var(--md-hue),15%,95%,0.12);--md-typeset-table-color--light:hsla(var(--md-hue),15%,95%,0.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-bg-color:hsla(var(--md-hue),15%,10%,0.87);--md-footer-bg-color--dark:hsla(var(--md-hue),15%,8%,1);--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #00000040,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0006,0 0 0.05rem #00000059;color-scheme:dark}[data-md-color-scheme=slate] img[src$="#gh-light-mode-only"],[data-md-color-scheme=slate] img[src$="#only-light"]{display:none}[data-md-color-scheme=slate][data-md-color-primary=pink]{--md-typeset-a-color:#ed5487}[data-md-color-scheme=slate][data-md-color-primary=purple]{--md-typeset-a-color:#c46fd3}[data-md-color-scheme=slate][data-md-color-primary=deep-purple]{--md-typeset-a-color:#a47bea}[data-md-color-scheme=slate][data-md-color-primary=indigo]{--md-typeset-a-color:#5488e8}[data-md-color-scheme=slate][data-md-color-primary=teal]{--md-typeset-a-color:#00ccb8}[data-md-color-scheme=slate][data-md-color-primary=green]{--md-typeset-a-color:#71c174}[data-md-color-scheme=slate][data-md-color-primary=deep-orange]{--md-typeset-a-color:#ff764d}[data-md-color-scheme=slate][data-md-color-primary=brown]{--md-typeset-a-color:#c1775c}[data-md-color-scheme=slate][data-md-color-primary=black],[data-md-color-scheme=slate][data-md-color-primary=blue-grey],[data-md-color-scheme=slate][data-md-color-primary=grey],[data-md-color-scheme=slate][data-md-color-primary=white]{--md-typeset-a-color:#5e8bde}[data-md-color-switching] *,[data-md-color-switching] :after,[data-md-color-switching] :before{transition-duration:0ms!important}}[data-md-color-accent=red]{--md-accent-fg-color:#ff1947;--md-accent-fg-color--transparent:#ff19471a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=pink]{--md-accent-fg-color:#f50056;--md-accent-fg-color--transparent:#f500561a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=purple]{--md-accent-fg-color:#df41fb;--md-accent-fg-color--transparent:#df41fb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=deep-purple]{--md-accent-fg-color:#7c4dff;--md-accent-fg-color--transparent:#7c4dff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=indigo]{--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=blue]{--md-accent-fg-color:#4287ff;--md-accent-fg-color--transparent:#4287ff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-blue]{--md-accent-fg-color:#0091eb;--md-accent-fg-color--transparent:#0091eb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=cyan]{--md-accent-fg-color:#00bad6;--md-accent-fg-color--transparent:#00bad61a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=teal]{--md-accent-fg-color:#00bda4;--md-accent-fg-color--transparent:#00bda41a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=green]{--md-accent-fg-color:#00c753;--md-accent-fg-color--transparent:#00c7531a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-green]{--md-accent-fg-color:#63de17;--md-accent-fg-color--transparent:#63de171a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=lime]{--md-accent-fg-color:#b0eb00;--md-accent-fg-color--transparent:#b0eb001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=yellow]{--md-accent-fg-color:#ffd500;--md-accent-fg-color--transparent:#ffd5001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=amber]{--md-accent-fg-color:#fa0;--md-accent-fg-color--transparent:#ffaa001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=orange]{--md-accent-fg-color:#ff9100;--md-accent-fg-color--transparent:#ff91001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=deep-orange]{--md-accent-fg-color:#ff6e42;--md-accent-fg-color--transparent:#ff6e421a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-primary=red]{--md-primary-fg-color:#ef5552;--md-primary-fg-color--light:#e57171;--md-primary-fg-color--dark:#e53734;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=pink]{--md-primary-fg-color:#e92063;--md-primary-fg-color--light:#ec417a;--md-primary-fg-color--dark:#c3185d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=purple]{--md-primary-fg-color:#ab47bd;--md-primary-fg-color--light:#bb69c9;--md-primary-fg-color--dark:#8c24a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=deep-purple]{--md-primary-fg-color:#7e56c2;--md-primary-fg-color--light:#9574cd;--md-primary-fg-color--dark:#673ab6;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=indigo]{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=blue]{--md-primary-fg-color:#2094f3;--md-primary-fg-color--light:#42a5f5;--md-primary-fg-color--dark:#1975d2;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-blue]{--md-primary-fg-color:#02a6f2;--md-primary-fg-color--light:#28b5f6;--md-primary-fg-color--dark:#0287cf;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=cyan]{--md-primary-fg-color:#00bdd6;--md-primary-fg-color--light:#25c5da;--md-primary-fg-color--dark:#0097a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=teal]{--md-primary-fg-color:#009485;--md-primary-fg-color--light:#26a699;--md-primary-fg-color--dark:#007a6c;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=green]{--md-primary-fg-color:#4cae4f;--md-primary-fg-color--light:#68bb6c;--md-primary-fg-color--dark:#398e3d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-green]{--md-primary-fg-color:#8bc34b;--md-primary-fg-color--light:#9ccc66;--md-primary-fg-color--dark:#689f38;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=lime]{--md-primary-fg-color:#cbdc38;--md-primary-fg-color--light:#d3e156;--md-primary-fg-color--dark:#b0b52c;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=yellow]{--md-primary-fg-color:#ffec3d;--md-primary-fg-color--light:#ffee57;--md-primary-fg-color--dark:#fbc02d;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=amber]{--md-primary-fg-color:#ffc105;--md-primary-fg-color--light:#ffc929;--md-primary-fg-color--dark:#ffa200;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=orange]{--md-primary-fg-color:#ffa724;--md-primary-fg-color--light:#ffa724;--md-primary-fg-color--dark:#fa8900;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=deep-orange]{--md-primary-fg-color:#ff6e42;--md-primary-fg-color--light:#ff8a66;--md-primary-fg-color--dark:#f4511f;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=brown]{--md-primary-fg-color:#795649;--md-primary-fg-color--light:#8d6e62;--md-primary-fg-color--dark:#5d4037;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=grey]{--md-primary-fg-color:#757575;--md-primary-fg-color--light:#9e9e9e;--md-primary-fg-color--dark:#616161;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=blue-grey]{--md-primary-fg-color:#546d78;--md-primary-fg-color--light:#607c8a;--md-primary-fg-color--dark:#455a63;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=light-green]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#72ad2e}[data-md-color-primary=lime]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#8b990a}[data-md-color-primary=yellow]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#b8a500}[data-md-color-primary=amber]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#d19d00}[data-md-color-primary=orange]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#e68a00}[data-md-color-primary=white]{--md-primary-fg-color:hsla(var(--md-hue),0%,100%,1);--md-primary-fg-color--light:hsla(var(--md-hue),0%,100%,0.7);--md-primary-fg-color--dark:hsla(var(--md-hue),0%,0%,0.07);--md-primary-bg-color:hsla(var(--md-hue),0%,0%,0.87);--md-primary-bg-color--light:hsla(var(--md-hue),0%,0%,0.54);--md-typeset-a-color:#4051b5}[data-md-color-primary=white] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=white] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}@media screen and (min-width:60em){[data-md-color-primary=white] .md-search__form{background-color:hsla(var(--md-hue),0%,0%,.07)}[data-md-color-primary=white] .md-search__form:hover{background-color:hsla(var(--md-hue),0%,0%,.32)}[data-md-color-primary=white] .md-search__input+.md-search__icon{color:hsla(var(--md-hue),0%,0%,.87)}}@media screen and (min-width:76.25em){[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid #00000012}}[data-md-color-primary=black]{--md-primary-fg-color:hsla(var(--md-hue),15%,9%,1);--md-primary-fg-color--light:hsla(var(--md-hue),15%,9%,0.54);--md-primary-fg-color--dark:hsla(var(--md-hue),15%,9%,1);--md-primary-bg-color:hsla(var(--md-hue),15%,100%,1);--md-primary-bg-color--light:hsla(var(--md-hue),15%,100%,0.7);--md-typeset-a-color:#4051b5}[data-md-color-primary=black] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=black] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}[data-md-color-primary=black] .md-header{background-color:hsla(var(--md-hue),15%,9%,1)}@media screen and (max-width:59.984375em){[data-md-color-primary=black] .md-nav__source{background-color:hsla(var(--md-hue),15%,11%,.87)}}@media screen and (max-width:76.234375em){html [data-md-color-primary=black] .md-nav--primary .md-nav__title[for=__drawer]{background-color:hsla(var(--md-hue),15%,9%,1)}}@media screen and (min-width:76.25em){[data-md-color-primary=black] .md-tabs{background-color:hsla(var(--md-hue),15%,9%,1)}} \ No newline at end of file diff --git a/6.2.X/css/timeago.css b/6.2.X/css/timeago.css new file mode 100755 index 00000000..f7ab7d69 --- /dev/null +++ b/6.2.X/css/timeago.css @@ -0,0 +1,15 @@ +/* + timeago output is dynamic, which breaks when you print a page. + + This CSS is only included when type: timeago + and ensures fallback to type "iso_date" when printing. + + */ + +.git-revision-date-localized-plugin-iso_date { display: none } + +@media print { + .git-revision-date-localized-plugin-iso_date { display: inline } + .git-revision-date-localized-plugin-timeago { display: none } +} + diff --git a/6.2.X/deployment/assets/add-policy.png b/6.2.X/deployment/assets/add-policy.png new file mode 100755 index 00000000..07546832 Binary files /dev/null and b/6.2.X/deployment/assets/add-policy.png differ diff --git a/6.2.X/deployment/assets/architecture.png b/6.2.X/deployment/assets/architecture.png new file mode 100755 index 00000000..95f6009b Binary files /dev/null and b/6.2.X/deployment/assets/architecture.png differ diff --git a/6.2.X/deployment/assets/cluster-mode.png b/6.2.X/deployment/assets/cluster-mode.png new file mode 100755 index 00000000..77126813 Binary files /dev/null and b/6.2.X/deployment/assets/cluster-mode.png differ diff --git a/6.2.X/deployment/assets/cluster.png b/6.2.X/deployment/assets/cluster.png new file mode 100755 index 00000000..35984d3f Binary files /dev/null and b/6.2.X/deployment/assets/cluster.png differ diff --git a/6.2.X/deployment/assets/connectors-status.png b/6.2.X/deployment/assets/connectors-status.png new file mode 100755 index 00000000..f0284d22 Binary files /dev/null and b/6.2.X/deployment/assets/connectors-status.png differ diff --git a/6.2.X/deployment/assets/connectors.png b/6.2.X/deployment/assets/connectors.png new file mode 100755 index 00000000..5691ecb7 Binary files /dev/null and b/6.2.X/deployment/assets/connectors.png differ diff --git a/6.2.X/deployment/assets/create-user.png b/6.2.X/deployment/assets/create-user.png new file mode 100755 index 00000000..3674299f Binary files /dev/null and b/6.2.X/deployment/assets/create-user.png differ diff --git a/6.2.X/deployment/assets/csv.png b/6.2.X/deployment/assets/csv.png new file mode 100755 index 00000000..f9ccc4c2 Binary files /dev/null and b/6.2.X/deployment/assets/csv.png differ diff --git a/6.2.X/deployment/assets/ilm-policy.png b/6.2.X/deployment/assets/ilm-policy.png new file mode 100755 index 00000000..9a5b4586 Binary files /dev/null and b/6.2.X/deployment/assets/ilm-policy.png differ diff --git a/6.2.X/deployment/assets/index-template.png b/6.2.X/deployment/assets/index-template.png new file mode 100755 index 00000000..1ac57c10 Binary files /dev/null and b/6.2.X/deployment/assets/index-template.png differ diff --git a/6.2.X/deployment/assets/indices.png b/6.2.X/deployment/assets/indices.png new file mode 100755 index 00000000..bfc5cac9 Binary files /dev/null and b/6.2.X/deployment/assets/indices.png differ diff --git a/6.2.X/deployment/assets/install-map-server.png b/6.2.X/deployment/assets/install-map-server.png new file mode 100755 index 00000000..7c0e3667 Binary files /dev/null and b/6.2.X/deployment/assets/install-map-server.png differ diff --git a/6.2.X/deployment/assets/managers.png b/6.2.X/deployment/assets/managers.png new file mode 100755 index 00000000..8d9747f7 Binary files /dev/null and b/6.2.X/deployment/assets/managers.png differ diff --git a/6.2.X/deployment/assets/options-map-server.png b/6.2.X/deployment/assets/options-map-server.png new file mode 100755 index 00000000..da1f1ab5 Binary files /dev/null and b/6.2.X/deployment/assets/options-map-server.png differ diff --git a/6.2.X/deployment/assets/playground-auth.png b/6.2.X/deployment/assets/playground-auth.png new file mode 100755 index 00000000..270bbce2 Binary files /dev/null and b/6.2.X/deployment/assets/playground-auth.png differ diff --git a/6.2.X/deployment/assets/playground-schema.png b/6.2.X/deployment/assets/playground-schema.png new file mode 100755 index 00000000..8d6f4f27 Binary files /dev/null and b/6.2.X/deployment/assets/playground-schema.png differ diff --git a/6.2.X/deployment/assets/playground.png b/6.2.X/deployment/assets/playground.png new file mode 100755 index 00000000..77111bf3 Binary files /dev/null and b/6.2.X/deployment/assets/playground.png differ diff --git a/6.2.X/deployment/assets/region-map-server.png b/6.2.X/deployment/assets/region-map-server.png new file mode 100755 index 00000000..5329f6d6 Binary files /dev/null and b/6.2.X/deployment/assets/region-map-server.png differ diff --git a/6.2.X/deployment/assets/rollover.png b/6.2.X/deployment/assets/rollover.png new file mode 100755 index 00000000..26040124 Binary files /dev/null and b/6.2.X/deployment/assets/rollover.png differ diff --git a/6.2.X/deployment/assets/settings-map-server.png b/6.2.X/deployment/assets/settings-map-server.png new file mode 100755 index 00000000..66ec3831 Binary files /dev/null and b/6.2.X/deployment/assets/settings-map-server.png differ diff --git a/6.2.X/deployment/assets/streams.png b/6.2.X/deployment/assets/streams.png new file mode 100755 index 00000000..ea51cb8f Binary files /dev/null and b/6.2.X/deployment/assets/streams.png differ diff --git a/6.2.X/deployment/assets/style-map-server.png b/6.2.X/deployment/assets/style-map-server.png new file mode 100755 index 00000000..bada5771 Binary files /dev/null and b/6.2.X/deployment/assets/style-map-server.png differ diff --git a/6.2.X/deployment/assets/taxii.png b/6.2.X/deployment/assets/taxii.png new file mode 100755 index 00000000..c03c5ad4 Binary files /dev/null and b/6.2.X/deployment/assets/taxii.png differ diff --git a/6.2.X/deployment/assets/templates.png b/6.2.X/deployment/assets/templates.png new file mode 100755 index 00000000..90814ec0 Binary files /dev/null and b/6.2.X/deployment/assets/templates.png differ diff --git a/6.2.X/deployment/assets/user-groups.png b/6.2.X/deployment/assets/user-groups.png new file mode 100755 index 00000000..6c54656b Binary files /dev/null and b/6.2.X/deployment/assets/user-groups.png differ diff --git a/6.2.X/deployment/assets/user-token.png b/6.2.X/deployment/assets/user-token.png new file mode 100755 index 00000000..c2d5cc8d Binary files /dev/null and b/6.2.X/deployment/assets/user-token.png differ diff --git a/6.2.X/deployment/assets/viewer-map-server.png b/6.2.X/deployment/assets/viewer-map-server.png new file mode 100755 index 00000000..e9640d2c Binary files /dev/null and b/6.2.X/deployment/assets/viewer-map-server.png differ diff --git a/6.2.X/deployment/authentication/index.html b/6.2.X/deployment/authentication/index.html new file mode 100755 index 00000000..f2e88d71 --- /dev/null +++ b/6.2.X/deployment/authentication/index.html @@ -0,0 +1,5640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Authentication - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Authentication

+

Introduction

+

OpenCTI supports several authentication providers. If you configure multiple strategies, they will be tested in the order you declared them.

+
+

Activation

+

You need to configure/activate only the authentication strategy that you really want to propose to your users.

+
+

The product proposes two kinds of authentication strategies:

+
    +
  • Form (asking user for a user/password),
  • +
  • Buttons (click with authentication on an external system).
  • +
+

Supported Strategies

+

Under the hood, we technically use the strategies provided by PassportJS. We integrate a subset of the strategies available with passport. If you need more, we can integrate other strategies.

+

Local users (form)

+

This strategy uses the OpenCTI database as a user management.

+

OpenCTI use this strategy as the default, but it's not the one we recommend for security reasons.

+
"local": {
+    "strategy": "LocalStrategy",
+    "config": {
+        "disabled": false
+    }
+}
+
+
+

Production deployment

+

Please use the LDAP/Auth0/OpenID/SAML strategy for production deployment.

+
+

LDAP (form)

+

This strategy can be used to authenticate your user with your company LDAP and is based on Passport - LDAPAuth.

+
"ldap": {
+    "strategy": "LdapStrategy",
+    "config": {
+        "url": "ldaps://mydc.domain.com:686",
+        "bind_dn": "cn=Administrator,cn=Users,dc=mydomain,dc=com",
+        "bind_credentials": "MY_STRONG_PASSWORD",
+        "search_base": "cn=Users,dc=mydomain,dc=com",
+        "search_filter": "(cn={{username}})",
+        "mail_attribute": "mail",
+        // "account_attribute": "givenName",
+        // "firstname_attribute": "cn",
+        // "lastname_attribute": "cn",
+        "account_attrgroup_search_filteribute": "givenName",
+        "allow_self_signed": true
+    }
+}
+
+

If you would like to use LDAP groups to automatically associate LDAP groups and OpenCTI groups/organizations:

+
"ldap": {
+    "config": {
+        ...
+        "group_search_base": "cn=Groups,dc=mydomain,dc=com",
+        "group_search_filter": "(member={{dn}})",
+        "groups_management": { // To map LDAP Groups to OpenCTI Groups
+            "group_attribute": "cn",
+            "groups_mapping": ["LDAP_Group_1:OpenCTI_Group_1", "LDAP_Group_2:OpenCTI_Group_2", ...]
+        },
+        "organizations_management": { // To map LDAP Groups to OpenCTI Organizations
+            "organizations_path": "cn",
+            "organizations_mapping": ["LDAP_Group_1:OpenCTI_Organization_1", "LDAP_Group_2:OpenCTI_Organization_2", ...]
+        }
+    }
+}
+
+

SAML (button)

+

This strategy can be used to authenticate your user with your company SAML and is based on Passport - SAML.

+
"saml": {
+    "identifier": "saml",
+    "strategy": "SamlStrategy",
+    "config": {
+        "issuer": "mytestsaml",
+        // "account_attribute": "nameID",
+        // "firstname_attribute": "nameID",
+        // "lastname_attribute": "nameID",
+        "entry_point": "https://auth.mydomain.com/auth/realms/mydomain/protocol/saml",
+        "saml_callback_url": "http://localhost:4000/auth/saml/callback",
+        // "private_key": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwg...",
+        "cert": "MIICmzCCAYMCBgF2Qt3X1zANBgkqhkiG9w0BAQsFADARMQ8w...",
+        "logout_remote": false
+    }
+}
+
+

For the SAML strategy to work:

+
    +
  • The cert parameter is mandatory (PEM format) because it is used to validate the SAML response.
  • +
  • The private_key (PEM format) is optional and is only required if you want to sign the SAML client request.
  • +
+
+

Certificates

+

Be careful to put the cert / private_key key in PEM format. Indeed, a lot of systems generally export the keys in X509 / PCKS12 formats and so you will need to convert them. +Here is an example to extract PEM from PCKS12: +

openssl pkcs12 -in keystore.p12 -out newfile.pem -nodes
+

+
+

Here is an example of SAML configuration using environment variables:

+
- PROVIDERS__SAML__STRATEGY=SamlStrategy 
+- "PROVIDERS__SAML__CONFIG__LABEL=Login with SAML"
+- PROVIDERS__SAML__CONFIG__ISSUER=mydomain
+- PROVIDERS__SAML__CONFIG__ENTRY_POINT=https://auth.mydomain.com/auth/realms/mydomain/protocol/saml
+- PROVIDERS__SAML__CONFIG__SAML_CALLBACK_URL=http://opencti.mydomain.com/auth/saml/callback
+- PROVIDERS__SAML__CONFIG__CERT=MIICmzCCAYMCBgF3Rt3X1zANBgkqhkiG9w0BAQsFADARMQ8w
+- PROVIDERS__SAML__CONFIG__LOGOUT_REMOTE=false
+
+

OpenCTI supports mapping SAML Roles/Groups on OpenCTI Groups. Here is an example:

+
"saml": {
+    "config": {
+        ...,
+        // Groups mapping
+        "groups_management": { // To map SAML Groups to OpenCTI Groups
+            "group_attributes": ["Group"],
+            "groups_mapping": ["SAML_Group_1:OpenCTI_Group_1", "SAML_Group_2:OpenCTI_Group_2", ...]
+        },
+        "groups_management": { // To map SAML Roles to OpenCTI Groups
+            "group_attributes": ["Role"],
+            "groups_mapping": ["SAML_Role_1:OpenCTI_Group_1", "SAML_Role_2:OpenCTI_Group_2", ...]
+        },
+        // Organizations mapping
+        "organizations_management": { // To map SAML Groups to OpenCTI Organizations
+            "organizations_path": ["Group"],
+            "organizations_mapping": ["SAML_Group_1:OpenCTI_Organization_1", "SAML_Group_2:OpenCTI_Organization_2", ...]
+        },
+        "organizations_management": { // To map SAML Roles to OpenCTI Organizations
+            "organizations_path": ["Role"],
+            "organizations_mapping": ["SAML_Role_1:OpenCTI_Organization_1", "SAML_Role_2:OpenCTI_Organization_2", ...]
+        }
+    }
+}
+
+

Here is an example of SAML Groups mapping configuration using environment variables:

+
- "PROVIDERS__SAML__CONFIG__GROUPS_MANAGEMENT__GROUP_ATTRIBUTES=[\"Group\"]"
+- "PROVIDERS__SAML__CONFIG__GROUPS_MANAGEMENT__GROUPS_MAPPING=[\"SAML_Group_1:OpenCTI_Group_1\", \"SAML_Group_2:OpenCTI_Group_2\", ...]"
+
+

Auth0 (button)

+

This strategy allows to use Auth0 Service to handle the authentication and is based on Passport - Auth0.

+
"authzero": {
+    "identifier": "auth0",
+    "strategy": "Auth0Strategy",
+    "config": {
+        "clientID": "XXXXXXXXXXXXXXXXXX",
+        "baseURL": "https://opencti.mydomain.com",
+        "clientSecret": "XXXXXXXXXXXXXXXXXX",
+        "callback_url": "https://opencti.mydomain.com/auth/auth0/callback",
+        "domain": "mycompany.eu.auth0.com",
+        "audience": "XXXXXXXXXXXXXXX",
+        "scope": "openid email profile XXXXXXXXXXXXXXX",
+        "logout_remote": false
+    }
+}
+
+

Here is an example of Auth0 configuration using environment variables:

+
- PROVIDERS__AUTHZERO__STRATEGY=Auth0Strategy
+- PROVIDERS__AUTHZERO__CONFIG__CLIENT_ID=${AUTH0_CLIENT_ID}
+- PROVIDERS__AUTHZERO__CONFIG__BASEURL=${AUTH0_BASE_URL}
+- PROVIDERS__AUTHZERO__CONFIG__CLIENT_SECRET=${AUTH0_CLIENT_SECRET}
+- PROVIDERS__AUTHZERO__CONFIG__CALLBACK_URL=${AUTH0_CALLBACK_URL}
+- PROVIDERS__AUTHZERO__CONFIG__DOMAIN=${AUTH0_DOMAIN}
+- "PROVIDERS__AUTHZERO__CONFIG__SCOPE=openid email profile"
+- PROVIDERS__AUTHZERO__CONFIG__LOGOUT_REMOTE=false
+
+

OpenID Connect (button)

+

This strategy allows to use the OpenID Connect Protocol to handle the authentication and is based on Node OpenID Client which is more powerful than the passport one.

+
"oic": {
+    "identifier": "oic",
+    "strategy": "OpenIDConnectStrategy",
+    "config": {
+        "label": "Login with OpenID",
+        "issuer": "https://auth.mydomain.com/auth/realms/mydomain",
+        "client_id": "XXXXXXXXXXXXXXXXXX",
+        "client_secret": "XXXXXXXXXXXXXXXXXX",
+        "redirect_uris": ["https://opencti.mydomain.com/auth/oic/callback"],
+        "logout_remote": false
+    }
+}
+
+

Here is an example of OpenID configuration using environment variables:

+
- PROVIDERS__OPENID__STRATEGY=OpenIDConnectStrategy 
+- "PROVIDERS__OPENID__CONFIG__LABEL=Login with OpenID"
+- PROVIDERS__OPENID__CONFIG__ISSUER=https://auth.mydomain.com/auth/realms/xxxx
+- PROVIDERS__OPENID__CONFIG__CLIENT_ID=XXXXXXXXXXXXXXXXXX
+- PROVIDERS__OPENID__CONFIG__CLIENT_SECRET=XXXXXXXXXXXXXXXXXX
+- "PROVIDERS__OPENID__CONFIG__REDIRECT_URIS=[\"https://opencti.mydomain.com/auth/oic/callback\"]"
+- PROVIDERS__OPENID__CONFIG__LOGOUT_REMOTE=false
+
+

OpenCTI support mapping OpenID Claims on OpenCTI Groups (everything is tied to a group in the platform). Here is an example:

+
"oic": {
+    "config": {
+        ...,
+        // Groups mapping
+        "groups_management": { // To map OpenID Claims to OpenCTI Groups
+            "groups_scope": "groups",
+            "groups_path": ["groups", "realm_access.groups", "resource_access.account.groups"],
+            "groups_mapping": ["OpenID_Group_1:OpenCTI_Group_1", "OpenID_Group_2:OpenCTI_Group_2", ...]
+        },
+        // Organizations mapping  
+        "organizations_management": { // To map OpenID Claims to OpenCTI Organizations
+            "organizations_scope": "groups",
+            "organizations_path": ["groups", "realm_access.groups", "resource_access.account.groups"],
+            "organizations_mapping": ["OpenID_Group_1:OpenCTI_Group_1", "OpenID_Group_2:OpenCTI_Group_2", ...]
+        },
+    }
+}
+
+

Here is an example of OpenID Groups mapping configuration using environment variables:

+
- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_SCOPE=groups
+- "PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_PATH=[\"groups\", \"realm_access.groups\", \"resource_access.account.groups\"]"
+- "PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_MAPPING=[\"OpenID_Group_1:OpenCTI_Group_1\", \"OpenID_Group_2:OpenCTI_Group_2\", ...]"
+
+

By default, the claims are mapped based on the content of the JWT access_token. If you want to map claims which are in other JWT (such as id_token), you can define the following environment variables:

+
- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__TOKEN_REFERENCE=id_token
+- PROVIDERS__OPENID__CONFIG__ORGANISATIONS_MANAGEMENT__TOKEN_REFERENCE=id_token
+
+

Alternatively, you can request OpenCTI to use claims from the userinfo endpoint instead of a JWT. +

- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__READ_USERINFO=true
+- PROVIDERS__OPENID__CONFIG__ORGANISATIONS_MANAGEMENT__READ_USERINFO=true
+

+

Facebook (button)

+

This strategy can authenticate your users with Facebook and is based on Passport - Facebook.

+
"facebook": {
+    "identifier": "facebook",
+    "strategy": "FacebookStrategy",
+    "config": {
+        "client_id": "XXXXXXXXXXXXXXXXXX",
+        "client_secret": "XXXXXXXXXXXXXXXXXX",
+        "callback_url": "https://opencti.mydomain.com/auth/facebook/callback",
+        "logout_remote": false
+    }
+}
+
+

Google (button)

+

This strategy can authenticate your users with Google and is based on Passport - Google.

+
"google": {
+    "identifier": "google",
+    "strategy": "GoogleStrategy",
+    "config": {
+        "client_id": "XXXXXXXXXXXXXXXXXX",
+        "client_secret": "XXXXXXXXXXXXXXXXXX",
+        "callback_url": "https://opencti.mydomain.com/auth/google/callback",
+        "logout_remote": false
+    }
+}
+
+

GitHub (button)

+

This strategy can authenticate your users with GitHub and is based on Passport - GitHub.

+
"github": {
+    "identifier": "github",
+    "strategy": "GithubStrategy",
+    "config": {
+        "client_id": "XXXXXXXXXXXXXXXXXX",
+        "client_secret": "XXXXXXXXXXXXXXXXXX",
+        "callback_url": "https://opencti.mydomain.com/auth/github/callback",
+        "logout_remote": false
+  }
+}
+
+

Client certificate (button)

+

This strategy can authenticate a user based on SSL client certificates. For this, you need to configure OpenCTI to start in HTTPS, for example:

+
"port": 443,
+"https_cert": {
+    "key": "/cert/server_key.pem",
+    "crt": "/cert/server_cert.pem",
+    "reject_unauthorized": true
+}
+
+

And then add the ClientCertStrategy:

+
"cert": {
+    "strategy":"ClientCertStrategy",
+    "config": {
+        "label":"CLIENT CERT"
+    }
+}
+
+

Afterwards, when accessing for the first time OpenCTI, the browser will ask for the certificate you want to use.

+

Proxy headers (automatic)

+

This strategy can authenticate the users directly from trusted headers.

+
{
+  "header": {
+    "strategy": "HeaderStrategy",
+    "config": {
+      "disabled": false,
+      "header_email": "auth_email_address",
+      "header_name": "auth_name",
+      "header_firstname": "auth_firstname",
+      "header_lastname": "auth_lastname",
+      "logout_uri": "https://www.filigran.io",
+      "groups_management": {
+        "groups_header": "auth_groups",
+        "groups_splitter": ",",
+        "groups_mapping": ["admin:admin", "root:root"]
+      },
+      "organizations_management": {
+        "organizations_header": "auth_institution",
+        "organizations_splitter": ",",
+        "organizations_mapping": ["test:test"]
+      }
+    }
+  }
+}
+
+

If this mode is activated and the headers are available, the user will be automatically logged without any action or notice. The logout uri will remove the session and redirect to the configured uri. If not specified, the redirect will be done to the request referer and so the header authentication will be done again.

+

Automatically create group on SSO

+

The variable auto_create_group can be added in the options of some strategies (LDAP, SAML and OpenID). If this variable is true, the groups of a user that logins will automatically be created if they don’t exist.

+

More precisely, if the user that tries to authenticate has groups that don’t exist in OpenCTI but exist in the SSO configuration, there are two cases:

+
    +
  • if auto_create_group= true in the SSO configuration: the groups are created at the platform initialization and the user will be mapped on them.
  • +
  • else: an error is raised.
  • +
+

Example

+

We assume that Group1 exists in the platform, and newGroup doesn’t exist. The user that tries to log in has the group newGroup. If auto_create_group = true in the SSO configuration, the group named newGroup will be created at the platform initialization and the user will be mapped on it. If auto_create_group = false or is undefined, the user can’t log in and an error is raised.

+
"groups_management": {
+  "group_attribute": "cn",
+  "groups_mapping": ["SSO_GROUP_NAME1:group1", "SSO_GROUP_NAME_2:newGroup", ...]
+},
+"auto_create_group": true
+
+

Examples

+

LDAP then fallback to local

+

In this example the users have a login form and need to enter login and password. The authentication is done on LDAP first, then locally if user failed to authenticate and finally fail if none of them succeeded. Here is an example for the production.json file:

+
"providers": {
+    "ldap": {
+        "strategy": "LdapStrategy",
+        "config": {
+            "url": "ldaps://mydc.mydomain.com:636",
+            "bind_dn": "cn=Administrator,cn=Users,dc=mydomain,dc=com",
+            "bind_credentials": "MY_STRONG_PASSWORD",
+            "search_base": "cn=Users,dc=mydomain,dc=com",
+            "search_filter": "(cn={{username}})",
+            "mail_attribute": "mail",
+            "account_attribute": "givenName"
+        }
+    },
+    "local": {
+        "strategy": "LocalStrategy",
+        "config": {
+            "disabled": false
+        }
+    }
+}
+
+

If you use a container deployment, here is an example using environment variables:

+
- PROVIDERS__LDAP__STRATEGY=LdapStrategy
+- PROVIDERS__LDAP__CONFIG__URL=ldaps://mydc.mydomain.org:636
+- PROVIDERS__LDAP__CONFIG__BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=com
+- PROVIDERS__LDAP__CONFIG__BIND_CREDENTIALS=XXXXXXXXXX
+- PROVIDERS__LDAP__CONFIG__SEARCH_BASE=cn=Users,dc=mydomain,dc=com
+- PROVIDERS__LDAP__CONFIG__SEARCH_FILTER=(cn={{username}})
+- PROVIDERS__LDAP__CONFIG__MAIL_ATTRIBUTE=mail
+- PROVIDERS__LDAP__CONFIG__ACCOUNT_ATTRIBUTE=givenName
+- PROVIDERS__LDAP__CONFIG__ALLOW_SELF_SIGNED=true
+- PROVIDERS__LOCAL__STRATEGY=LocalStrategy
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/clustering/index.html b/6.2.X/deployment/clustering/index.html new file mode 100755 index 00000000..938135d9 --- /dev/null +++ b/6.2.X/deployment/clustering/index.html @@ -0,0 +1,5226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clustering - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Clustering

+

Introduction

+

The OpenCTI platform technological stack has been designed to be able to scale horizontally. All dependencies such as Elastic or Redis can be deployed in cluster mode and performances can be drastically increased by deploying multiple platform and worker instances.

+

High level architecture

+

Here is the high level architecture for customers and Filigran cloud platform to ensure both high availability and throughput.

+

Cluster

+

Configuration

+

Dependencies

+

ElasticSearch

+

In the ElasticSearch configuration of OpenCTI, it is possible to declare all nodes.

+
- "ELASTICSEARCH__URL=[\"https://user:pass@node1:9200\", \"https://user:pass@node2:9200\", ...]"
+
+
+

Compatibility

+

OpenCTI is also compatible with OpenSearch and AWS / GCP / Azure native search services based on the ElasticSearch query language.

+
+

Redis

+

Redis should be turned to cluster mode:

+
- REDIS__MODE=cluster
+- "REDIS__HOSTNAMES=[\"node1:6379\", \"node2:6379\", ...]"
+
+
+

Compatibility

+

OpenCTI is also compatible with ElastiCache, MemoryStore and AWS / GCP / Azure native services based on the Redis protocol.

+
+

RabbitMQ

+

For the RabbitMQ cluster, you will need a TCP load balancer on top of the nodes since the configuration does not support multi-nodes for now:

+
- RABBITMQ__HOSTNAME=load-balancer-rabbitmq
+
+
+

Compatibility

+

OpenCTI is also compatible with Amazon MQ, CloudAMQP and AWS / GCP / Azure native services based on the AMQP protocol.

+
+

S3 bucket / MinIO

+

MinIO is an open source server able to serve S3 buckets. It can be deployed in cluster mode and is compatible with several storage backend. OpenCTI is compatible with any tool following the S3 standard.

+

Platform

+

As showed on the schema, best practices for cluster mode and to avoid any congestion in the technological stack are:

+
    +
  • Deploy platform(s) dedicated to end users and connectors registration
  • +
  • Deploy platform(s) dedicated to workers / ingestion process
      +
    • We recommend 3 to 4 workers maximum by OpenCTI instance.
    • +
    • The ingestion platforms will never be accessed directly by end users.
    • +
    +
  • +
+

When enabling clustering, the number of nodes is displayed in Settings > Parameters.

+

Cluster mode

+

Managers and schedulers

+

Also, since some managers like the rule engine, the task manager and the notification manager can take some resources in the OpenCTI NodeJS process, it is highly recommended to disable them in the frontend cluster. OpenCTI automatically handle the distribution and the launching of the engines across all nodes in the cluster except where they are explicitly disabled in the configuration.

+

Managers

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/configuration/index.html b/6.2.X/deployment/configuration/index.html new file mode 100755 index 00000000..6bd8d959 --- /dev/null +++ b/6.2.X/deployment/configuration/index.html @@ -0,0 +1,7250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configuration - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Configuration

+

The purpose of this section is to learn how to configure OpenCTI to have it tailored for your production and development needs. It is possible to check all default parameters implemented in the platform in the default.json file.

+

Here are the configuration keys, for both containers (environment variables) and manual deployment.

+
+

Parameters equivalence

+

The equivalent of a config variable in environment variables is the usage of a double underscores (__) for a level of config.

+

For example: +

"providers": {
+  "ldap": {
+    "strategy": "LdapStrategy"
+  }
+}
+

+

will become: +

PROVIDERS__LDAP__STRATEGY=LdapStrategy
+

+

If you need to put a list of elements for the key, it must have a special formatting. Here is an example for redirect URIs for OpenID config: +

"PROVIDERS__OPENID__CONFIG__REDIRECT_URIS=[\"https://demo.opencti.io/auth/oic/callback\"]"
+

+
+

Platform

+

API & Frontend

+

Basic parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:portAPP__PORT4000Listen port of the application
app:base_pathAPP__BASE_PATHSpecific URI (ie. /opencti)
app:base_urlAPP__BASE_URLhttp://localhost:4000Full URL of the platform (should include the base_path if any)
app:request_timeoutAPP__REQUEST_TIMEOUT1200000Request timeout, in ms (default 20 minutes)
app:session_timeoutAPP__SESSION_TIMEOUT1200000Session timeout, in ms (default 20 minutes)
app:session_idle_timeoutAPP__SESSION_IDLE_TIMEOUT0Idle timeout (locking the screen), in ms (default 0 minute - disabled)
app:session_cookieAPP__SESSION_COOKIEfalseUse memory/session cookie instead of persistent one
app:admin:emailAPP__ADMIN__EMAILadmin@opencti.ioDefault login email of the admin user
app:admin:passwordAPP__ADMIN__PASSWORDChangeMeDefault password of the admin user
app:admin:tokenAPP__ADMIN__TOKENChangeMeDefault token (must be a valid UUIDv4)
app:health_access_keyAPP__HEALTH_ACCESS_KEYChangeMeAccess key for the /health endpoint. Must be changed - will not respond to default value. Access with /health?health_access_key=ChangeMe
+

Network and security

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
http_proxyHTTP_PROXYProxy URL for HTTP connection (example: http://proxy:80080)
https_proxyHTTPS_PROXYProxy URL for HTTPS connection (example: http://proxy:80080)
no_proxyNO_PROXYComma separated list of hostnames for proxy exception (example: localhost,127.0.0.0/8,internal.opencti.io)
app:https_cert:cookie_secureAPP__HTTPS_CERT__COOKIE_SECUREfalseSet the flag "secure" for session cookies.
app:https_cert:caAPP__HTTPS_CERT__CAEmpty list []Certificate authority paths or content, only if the client uses a self-signed certificate.
app:https_cert:keyAPP__HTTPS_CERT__KEYCertificate key path or content
app:https_cert:crtAPP__HTTPS_CERT__CRTCertificate crt path or content
app:https_cert:reject_unauthorizedAPP__HTTPS_CERT__REJECT_UNAUTHORIZEDIf not false, the server certificate is verified against the list of supplied CAs
+

Logging

+
Errors
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:app_logs:logs_levelAPP__APP_LOGS__LOGS_LEVELinfoThe application log level
app:app_logs:logs_filesAPP__APP_LOGS__LOGS_FILEStrueIf application logs is logged into files
app:app_logs:logs_consoleAPP__APP_LOGS__LOGS_CONSOLEtrueIf application logs is logged to console (useful for containers)
app:app_logs:logs_max_filesAPP__APP_LOGS__LOGS_MAX_FILES7Maximum number of daily files in logs
app:app_logs:logs_directoryAPP__APP_LOGS__LOGS_DIRECTORY./logsFile logs directory
+
Audit
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:audit_logs:logs_filesAPP__AUDIT_LOGS__LOGS_FILEStrueIf audit logs is logged into files
app:audit_logs:logs_consoleAPP__AUDIT_LOGS__LOGS_CONSOLEtrueIf audit logs is logged to console (useful for containers)
app:audit_logs:logs_max_filesAPP__AUDIT_LOGS__LOGS_MAX_FILES7Maximum number of daily files in logs
app:audit_logs:logs_directoryAPP__AUDIT_LOGS__LOGS_DIRECTORY./logsAudit logs directory
+

Telemetry

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:telemetry:metrics:enabledAPP__TELEMETRY__METRICS__ENABLEDfalseEnable the metrics collection.
app:telemetry:metrics:exporter_otlpAPP__TELEMETRY__METRICS__EXPORTER_OTLPPort to expose the OTLP endpoint.
app:telemetry:metrics:exporter_prometheusAPP__TELEMETRY__METRICS__EXPORTER_PROMETHEUS14269Port to expose the Prometheus endpoint.
+

Maps & references

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:map_tile_server_darkAPP__MAP_TILE_SERVER_DARKhttps://map.opencti.io/styles/filigran-dark2/{z}/{x}/{y}.pngThe address of the OpenStreetMap provider with dark theme style
app:map_tile_server_lightAPP__MAP_TILE_SERVER_LIGHThttps://map.opencti.io/styles/filigran-light2/{z}/{x}/{y}.pngThe address of the OpenStreetMap provider with light theme style
app:reference_attachmentAPP__REFERENCE_ATTACHMENTfalseExternal reference mandatory attachment
+

Functional customization

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:artifact_zip_passwordAPP__ARTIFACT_ZIP_PASSWORDinfectedArtifact encrypted archive default password
relations_deduplication:past_daysRELATIONS_DEDUPLICATION__PAST_DAYS30De-duplicate relations based on start_time and stop_time - n days
relations_deduplication:next_daysRELATIONS_DEDUPLICATION__NEXT_DAYS30De-duplicate relations based on start_time and stop_time + n days
relations_deduplication:created_by_basedRELATIONS_DEDUPLICATION__CREATED_BY_BASEDfalseTake into account the author to duplicate even if stat_time / stop_time are matching
relations_deduplication:types_overrides:relationship_type:past_daysRELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__PAST_DAYSOverride the past days for a specific type of relationship (ex. targets)
relations_deduplication:types_overrides:relationship_type:next_daysRELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__NEXT_DAYSOverride the next days for a specific type of relationship (ex. targets)
relations_deduplication:types_overrides:relationship_type:created_by_basedRELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__CREATED_BY_BASEDOverride the author duplication for a specific type of relationship (ex. targets)
+

Technical customization

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
app:graphql:playground:enabledAPP__GRAPHQL__PLAYGROUND__ENABLEDtrueEnable the playground on /graphql
app:graphql:playground:force_disabled_introspectionAPP__GRAPHQL__PLAYGROUND__FORCE_DISABLED_INTROSPECTIONtrueIntrospection is allowed to auth users but can be disabled in needed
app:concurrency:retry_countAPP__CONCURRENCY__RETRY_COUNT200Number of try to get the lock to work an element (create/update/merge, ...)
app:concurrency:retry_delayAPP__CONCURRENCY__RETRY_DELAY100Delay between 2 lock retry (in milliseconds)
app:concurrency:retry_jitterAPP__CONCURRENCY__RETRY_JITTER50Random jitter to prevent concurrent retry (in milliseconds)
app:concurrency:max_ttlAPP__CONCURRENCY__MAX_TTL30000Global maximum time for lock retry (in milliseconds)
+

Dependencies

+

XTM Suite

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
xtm:openbas_urlXTM__OPENBAS_URLOpenBAS URL
xtm:openbas_tokenXTM__OPENBAS_TOKENOpenBAS token
xtm:openbas_reject_unauthorizedXTM__OPENBAS_REJECT_UNAUTHORIZEDfalseEnable TLS certificate check
xtm:openbas_disable_displayXTM__OPENBAS_DISABLE_DISPLAYfalseDisable OpenBAS posture in the UI
+

ElasticSearch

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
elasticsearch:engine_selectorELASTICSEARCH__ENGINE_SELECTORautoelk or opensearch, default is auto, please put elk if you use token auth.
elasticsearch:engine_checkELASTICSEARCH__ENGINE_CHECKfalseDisable Search Engine compatibility matrix verification.
Caution: OpenCTI was developed in compliance with the compatibility matrix. Setting the parameter to true may result in negative impacts.
elasticsearch:urlELASTICSEARCH__URLhttp://localhost:9200URL(s) of the ElasticSearch (supports http://user:pass@localhost:9200 and list of URLs)
elasticsearch:usernameELASTICSEARCH__USERNAMEUsername can be put in the URL or with this parameter
elasticsearch:passwordELASTICSEARCH__PASSWORDPassword can be put in the URL or with this parameter
elasticsearch:api_keyELASTICSEARCH__API_KEYAPI key for ElasticSearch token auth. Please set also engine_selector to elk
elasticsearch:index_prefixELASTICSEARCH__INDEX_PREFIXopenctiPrefix for the indices
elasticsearch:ssl:reject_unauthorizedELASTICSEARCH__SSL__REJECT_UNAUTHORIZEDtrueEnable TLS certificate check
elasticsearch:ssl:caELASTICSEARCH__SSL__CACustom certificate path or content
elasticsearch:search_wildcard_prefixELASTICSEARCH__SEARCH_WILDCARD_PREFIXfalseSearch includes words with automatic fuzzy comparison
elasticsearch:search_fuzzyELASTICSEARCH__SEARCH_FUZZYfalseSearch will include words not starting with the search keyword
+

Redis

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
redis:modeREDIS__MODEsingleConnect to redis in "single", "sentinel or "cluster" mode
redis:namespaceREDIS__NAMESPACENamespace (to use as prefix)
redis:hostnameREDIS__HOSTNAMElocalhostHostname of the Redis Server
redis:hostnamesREDIS__HOSTNAMESHostnames definition for Redis cluster or sentinel mode: a list of host:port objects.
redis:portREDIS__PORT6379Port of the Redis Server
redis:sentinel_master_nameREDIS__SENTINEL_MASTER_NAMEName of your Redis Sentinel Master (mandatory in sentinel mode)
redis:use_sslREDIS__USE_SSLfalseIs the Redis Server has TLS enabled
redis:usernameREDIS__USERNAMEUsername of the Redis Server
redis:passwordREDIS__PASSWORDPassword of the Redis Server
redis:caREDIS__CA[]List of path(s) of the CA certificate(s)
redis:trimmingREDIS__TRIMMING2000000Number of elements to maintain in the stream. (0 = unlimited)
+

RabbitMQ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
rabbitmq:hostnameRABBITMQ__HOSTNAMElocalhost 7Hostname of the RabbitMQ server
rabbitmq:portRABBITMQ__PORT5672Port of the RabbitMQ server
rabbitmq:port_managementRABBITMQ__PORT_MANAGEMENT15672Port of the RabbitMQ Management Plugin
rabbitmq:usernameRABBITMQ__USERNAMEguestRabbitMQ user
rabbitmq:passwordRABBITMQ__PASSWORDguestRabbitMQ password
rabbitmq:queue_typeRABBITMQ__QUEUE_TYPE"classic"RabbitMQ Queue Type ("classic" or "quorum")
----
rabbitmq:use_sslRABBITMQ__USE_SSLfalseUse TLS connection
rabbitmq:use_ssl_certRABBITMQ__USE_SSL_CERTPath or cert content
rabbitmq:use_ssl_keyRABBITMQ__USE_SSL_KEYPath or key content
rabbitmq:use_ssl_pfxRABBITMQ__USE_SSL_PFXPath or pfx content
rabbitmq:use_ssl_caRABBITMQ__USE_SSL_CA[]List of path(s) of the CA certificate(s)
rabbitmq:use_ssl_passphraseRABBITMQ__SSL_PASSPHRASEPassphrase for the key certificate
rabbitmq:use_ssl_reject_unauthorizedRABBITMQ__SSL_REJECT_UNAUTHORIZEDfalseReject rabbit self signed certificate
----
rabbitmq:management_sslRABBITMQ__MANAGEMENT_SSLfalseIs the Management Plugin has TLS enabled
rabbitmq:management_ssl_reject_unauthorizedRABBITMQ__SSL_REJECT_UNAUTHORIZEDtrueReject management self signed certificate
+

S3 Bucket

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
minio:endpointMINIO__ENDPOINTlocalhostHostname of the S3 Service. Example if you use AWS Bucket S3: s3.us-east-1.amazonaws.com (if minio:bucket_region value is us-east-1). This parameter value can be omitted if you use Minio as an S3 Bucket Service.
minio:portMINIO__PORT9000Port of the S3 Service. For AWS Bucket S3 over HTTPS, this value can be changed (usually 443).
minio:use_sslMINIO__USE_SSLfalseIndicates whether the S3 Service has TLS enabled. For AWS Bucket S3 over HTTPS, this value could be true.
minio:access_keyMINIO__ACCESS_KEYChangeMeAccess key for the S3 Service.
minio:secret_keyMINIO__SECRET_KEYChangeMeSecret key for the S3 Service.
minio:bucket_nameMINIO__BUCKET_NAMEopencti-bucketS3 bucket name. Useful to change if you use AWS.
minio:bucket_regionMINIO__BUCKET_REGIONus-east-1Region of the S3 bucket if you are using AWS. This parameter value can be omitted if you use Minio as an S3 Bucket Service.
minio:use_aws_roleMINIO__USE_AWS_ROLEfalseIndicates whether to use AWS role auto credentials. When this parameter is configured, the minio:access_key and minio:secret_key parameters are not necessary.
+

SMTP Service

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
smtp:hostnameSMTP__HOSTNAMESMTP Server hostname
smtp:portSMTP__PORT465SMTP Port (25 or 465 for TLS)
smtp:use_sslSMTP__USE_SSLfalseSMTP over TLS
smtp:reject_unauthorizedSMTP__REJECT_UNAUTHORIZEDfalseEnable TLS certificate check
smtp:usernameSMTP__USERNAMESMTP Username if authentication is needed
smtp:passwordSMTP__PASSWORDSMTP Password if authentication is needed
+

AI Service

+
+

AI deployment and cloud services

+

There are several possibilities for Enterprise Edition customers to use OpenCTI AI endpoints:

+
    +
  • Use the Filigran AI Service leveraging our custom AI model using the token given by the support team.
  • +
  • Use OpenAI or MistralAI cloud endpoints using your own tokens.
  • +
  • Deploy or use local AI endpoints (Filigran can provide you with the custom model).
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
ai:enabledAI__ENABLEDtrueEnable AI capabilities
ai:typeAI__TYPEmistralaiAI type (mistralai or openai)
ai:endpointAI__ENDPOINTEndpoint URL (empty means default cloud service)
ai:tokenAI__TOKENToken for endpoint credentials
ai:modelAI__MODELModel to be used for text generation (depending on type)
ai:model_imagesAI__MODEL_IMAGESModel to be used for image generation (depending on type)
+

Using a credentials provider

+

In some cases, it may not be possible to put directly dependencies credentials directly in environment variables or static configuration. The platform can then retrieve them from a credentials provider. Here is the list of supported providers:

+ + + + + + + + + + + + + +
Credentials providerProvider key
CyberArkcyberark
+

For each dependency, special configuration keys are available to ensure the platform retrieves credentials during start process. Not all dependencies support this mechanism, here is the exhaustive list:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
DependencyPrefix
ElasticSearchelasticsearch
S3 Storageminio
Redisredis
OpenID secretsoic
+
Common configurations
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
{prefix}:credentials_provider:https_cert:reject_unauthorized{PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__REJECT_UNAUTHORIZEDfalseReject unauthorized TLS connection
{prefix}:credentials_provider:https_cert:crt{PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__CRTPath to the HTTPS certificate
{prefix}:credentials_provider:https_cert:key{PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__KEYPath to the HTTPS key
{prefix}:credentials_provider:https_cert:ca{PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__CAPath to the HTTPS CA certificate
+
CyberArk
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
{prefix}:credentials_provider:cyberark:uri{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__URIThe URL of the CyberArk endpoint for credentials retrieval (GET request)
{prefix}:credentials_provider:cyberark:app_id{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__APP_IDThe used application ID for the dependency within CyberArk
{prefix}:credentials_provider:cyberark:safe{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__SAFEThe used safe key for the dependency within CyberArk
{prefix}:credentials_provider:cyberark:object{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__OBJECTThe used object key for the dependency within CyberArk
{prefix}:credentials_provider:cyberark:default_splitter{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER:Default splitter of the credentials results, for "username:password", default is ":"
{prefix}:credentials_provider:cyberark:field_targets{PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS[]Fields targets in the data content response after splitting
+

Here is an example for ElasticSearch:

+

Environment variables: +

- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__URI=http://my.cyberark.com/AIMWebService/api/Accounts
+- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__APP_ID=opencti-elastic
+- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__SAFE=mysafe-key
+- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__OBJECT=myobject-key
+- "ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER=:" # As default is already ":", may not be necessary
+- "ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS=[\"username\",\"password\"]"
+

+

JSON version: +

"elasticsearch": {
+    "credentials_provider": {
+        "cyberark": {
+            "uri": "http://my.cyberark.com/AIMWebService/api/Accounts",
+            "app_id": "opencti-elastic",
+            "safe": "mysafe-key",
+            "object": "myobject-key",
+            "default_splitter": ":",
+            "field_targets": ["username", "password"]
+      }
+    }
+}
+

+

Another example for MinIo (S3) using certificate:

+

Environment variables: +

- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__CRT=/cert_volume/mycert.crt
+- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__KEY=/cert_volume/mycert.key
+- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__CA=/cert_volume/ca.crt
+- MINIO__CREDENTIALS_PROVIDER__CYBERARK__URI=http://my.cyberark.com/AIMWebService/api/Accounts
+- MINIO__CREDENTIALS_PROVIDER__CYBERARK__APP_ID=opencti-s3
+- MINIO__CREDENTIALS_PROVIDER__CYBERARK__SAFE=mysafe-key
+- MINIO__CREDENTIALS_PROVIDER__CYBERARK__OBJECT=myobject-key
+- "MINIO__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER=:" # As default is already ":", may not be necessary
+- "MINIO__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS=[\"access_key\",\"secret_key\"]"
+

+

Engines, Schedules and Managers

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
rule_engine:enabledRULE_ENGINE__ENABLEDtrueEnable/disable the rule engine
rule_engine:lock_keyRULE_ENGINE__LOCK_KEYrule_engine_lockLock key of the engine in Redis
----
history_manager:enabledHISTORY_MANAGER__ENABLEDtrueEnable/disable the history manager
history_manager:lock_keyHISTORY_MANAGER__LOCK_KEYhistory_manager_lockLock key for the manager in Redis
----
task_scheduler:enabledTASK_SCHEDULER__ENABLEDtrueEnable/disable the task scheduler
task_scheduler:lock_keyTASK_SCHEDULER__LOCK_KEYtask_manager_lockLock key for the scheduler in Redis
task_scheduler:intervalTASK_SCHEDULER__INTERVAL10000Interval to check new task to do (in ms)
----
sync_manager:enabledSYNC_MANAGER__ENABLEDtrueEnable/disable the sync manager
sync_manager:lock_keySYNC_MANAGER__LOCK_KEYsync_manager_lockLock key for the manager in Redis
sync_manager:intervalSYNC_MANAGER__INTERVAL10000Interval to check new sync feeds to consume (in ms)
----
expiration_scheduler:enabledEXPIRATION_SCHEDULER__ENABLEDtrueEnable/disable the scheduler
expiration_scheduler:lock_keyEXPIRATION_SCHEDULER__LOCK_KEYexpired_manager_lockLock key for the scheduler in Redis
expiration_scheduler:intervalEXPIRATION_SCHEDULER__INTERVAL300000Interval to check expired indicators (in ms)
----
retention_manager:enabledRETENTION_MANAGER__ENABLEDtrueEnable/disable the retention manager
retention_manager:lock_keyRETENTION_MANAGER__LOCK_KEYretention_manager_lockLock key for the manager in Redis
retention_manager:intervalRETENTION_MANAGER__INTERVAL60000Interval to check items to be deleted (in ms)
----
notification_manager:enabledNOTIFICATION_MANAGER__ENABLEDtrueEnable/disable the notification manager
notification_manager:lock_live_keyNOTIFICATION_MANAGER__LOCK_LIVE_KEYnotification_live_manager_lockLock live key for the manager in Redis
notification_manager:lock_digest_keyNOTIFICATION_MANAGER__LOCK_DIGEST_KEYnotification_digest_manager_lockLock digest key for the manager in Redis
notification_manager:intervalNOTIFICATION_MANAGER__INTERVAL10000Interval to push notifications
----
publisher_manager:enabledPUBLISHER_MANAGER__ENABLEDtrueEnable/disable the publisher manager
publisher_manager:lock_keyPUBLISHER_MANAGER__LOCK_KEYpublisher_manager_lockLock key for the manager in Redis
publisher_manager:intervalPUBLISHER_MANAGER__INTERVAL10000Interval to send notifications / digests (in ms)
----
ingestion_manager:enabledINGESTION_MANAGER__ENABLEDtrueEnable/disable the ingestion manager
ingestion_manager:lock_keyINGESTION_MANAGER__LOCK_KEYingestion_manager_lockLock key for the manager in Redis
ingestion_manager:intervalINGESTION_MANAGER__INTERVAL300000Interval to check for new data in remote feeds
----
playbook_manager:enabledPLAYBOOK_MANAGER__ENABLEDtrueEnable/disable the playbook manager
playbook_manager:lock_keyPLAYBOOK_MANAGER__LOCK_KEYpublisher_manager_lockLock key for the manager in Redis
playbook_manager:intervalPLAYBOOK_MANAGER__INTERVAL60000Interval to check new playbooks
----
activity_manager:enabledACTIVITY_MANAGER__ENABLEDtrueEnable/disable the activity manager
activity_manager:lock_keyACTIVITY_MANAGER__LOCK_KEYactivity_manager_lockLock key for the manager in Redis
----
connector_manager:enabledCONNECTOR_MANAGER__ENABLEDtrueEnable/disable the connector manager
connector_manager:lock_keyCONNECTOR_MANAGER__LOCK_KEYconnector_manager_lockLock key for the manager in Redis
connector_manager:works_day_rangeCONNECTOR_MANAGER__WORKS_DAY_RANGE7Days range before considering the works as too old
connector_manager:intervalCONNECTOR_MANAGER__INTERVAL10000Interval to check the state of the works
----
import_csv_built_in_connector:enabledIMPORT_CSV_CONNECTOR__ENABLEDtrueEnable/disable the csv import connector
import_csv_built_in_connector:validate_before_importIMPORT_CSV_CONNECTOR__VALIDATE_BEFORE_IMPORTfalseValidates the bundle before importing
----
file_index_manager:enabledFILE_INDEX_MANAGER__ENABLEDtrueEnable/disable the file indexing manager
file_index_manager:stream_lock_keyFILE_INDEX_MANAGER__STREAM_LOCKfile_index_manager_stream_lockStream lock key for the manager in Redis
file_index_manager:intervalFILE_INDEX_MANAGER__INTERVAL60000Interval to check for new files
----
indicator_decay_manager:enabledINDICATOR_DECAY_MANAGER__ENABLEDtrueEnable/disable the indicator decay manager
indicator_decay_manager:lock_keyINDICATOR_DECAY_MANAGER__LOCK_KEYindicator_decay_manager_lockLock key for the manager in Redis
indicator_decay_manager:intervalINDICATOR_DECAY_MANAGER__INTERVAL60000Interval to check for indicators to update
indicator_decay_manager:batch_sizeINDICATOR_DECAY_MANAGER__BATCH_SIZE10000Number of indicators handled by the manager
----
garbage_collection_manager:enabledGARBAGE_COLLECTION_MANAGER__ENABLEDtrueEnable/disable the trash manager
garbage_collection_manager:lock_keyGARBAGE_COLLECTION_MANAGER__LOCK_KEYgarbage_collection_manager_lockLock key for the manager in Redis
garbage_collection_manager:intervalGARBAGE_COLLECTION_MANAGER__INTERVAL60000Interval to check for trash elements to delete
garbage_collection_manager:batch_sizeGARBAGE_COLLECTION_MANAGER__BATCH_SIZE10000Number of trash elements to delete at once
garbage_collection_manager:deleted_retention_daysGARBAGE_COLLECTION_MANAGER__DELETED_RETENTION_DAYS7Days after which elements in trash are deleted
----
telemetry_manager:lock_keyTELEMETRY_MANAGER__LOCK_LOCKtelemetry_manager_lockLock key for the manager in Redis
+
+

Manager's duties

+

A description of each manager's duties is available on a dedicated page.

+
+

Worker and connector

+

Can be configured manually using the configuration file config.yml or through environment variables.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
opencti:urlOPENCTI_URLThe URL of the OpenCTI platform
opencti:tokenOPENCTI_TOKENA token of an administrator account with bypass capability
----
mq:use_ssl//Depending of the API configuration (fetch from API)
mq:use_ssl_caMQ_USE_SSL_CAPath or ca content
mq:use_ssl_certMQ_USE_SSL_CERTPath or cert content
mq:use_ssl_keyMQ_USE_SSL_KEYPath or key content
mq:use_ssl_passphraseMQ_USE_SSL_PASSPHRASEPassphrase for the key certificate
mq:use_ssl_reject_unauthorizedMQ_USE_SSL_REJECT_UNAUTHORIZEDfalseReject rabbit self signed certificate
+

Worker specific configuration

+

Logging

+ + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
worker:log_levelWORKER_LOG_LEVELinfoThe log level (error, warning, info or debug)
+

Telemetry

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableDefault valueDescription
worker:telemetry_enabledWORKER_TELEMETRY_ENABLEDfalseEnable the Prometheus endpoint
worker:telemetry_prometheus_portWORKER_PROMETHEUS_TELEMETRY_PORT14270Port of the Prometheus endpoint
worker:telemetry_prometheus_hostWORKER_PROMETHEUS_TELEMETRY_HOST0.0.0.0Listen address of the Prometheus endpoint
+

Connector specific configuration

+

For specific connector configuration, you need to check each connector behavior.

+

ElasticSearch

+

If you want to adapt the memory consumption of ElasticSearch, you can use these options:

+
# Add the following environment variable:
+"ES_JAVA_OPTS=-Xms8g -Xmx8g"
+
+

This can be done in configuration file in the jvm.conf file.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/connectors/index.html b/6.2.X/deployment/connectors/index.html new file mode 100755 index 00000000..5670399f --- /dev/null +++ b/6.2.X/deployment/connectors/index.html @@ -0,0 +1,5573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Connectors - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Connectors

+

Introduction

+
+

Connectors list

+

You are looking for the available connectors? The list is in the OpenCTI Ecosystem.

+
+

Connectors are the cornerstone of the OpenCTI platform and allow organizations to easily ingest, enrich or export data. According to their functionality and use case, they are categorized in the following classes.

+

Connectors

+

Import

+

These connectors automatically retrieve information from an external organization, application, or service, and convert it to STIX 2.1 bundles. Then, they import it into OpenCTI using the workers.

+

Enrichment

+

When a new object is created in the platform or on the user request, it is possible to trigger the internal enrichment connector to lookup and/or search the object in external organizations, applications, or services. If the object is found, the connectors will generate a STIX 2.1 bundle which will increase the level of knowledge about the concerned object.

+

+

Stream

+

These connectors connect to a platform live stream and continuously do something with the received events. In most cases, they are used to consume OpenCTI data and insert them in third-party platforms such as SIEMs, XDRs, EDRs, etc. In some cases, stream connectors can also query the external system on a regular basis and act as import connector for instance to gather alerts and sightings related to CTI data and push them to OpenCTI (bi-directional).

+

Import files

+

Information from an uploaded file can be extracted and ingested into OpenCTI. Examples are files attached to a report or a STIX 2.1 file.

+

Export files

+

Information stored in OpenCTI can be extracted into different file formats like .csv or .json (STIX 2.1).

+

Connector configuration

+

Connector users and tokens

+

All connectors have to be able to access the OpenCTI API. To allow this connection, they have 2 mandatory configuration parameters, the OPENCTI_URL and the OPENCTI_TOKEN.

+
+

Connectors tokens

+

Be careful, we strongly recommend to use a dedicated token for each connector running in the platform. So you have to create a specific user for each of them.

+

Also, if all connectors users can run with a user belonging to the Connectors group (with the Connector role), the Internal Export Files should be run with a user who is Administrator (with bypass capability) because they impersonate the user requesting the export to avoid data leak.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeRequired roleUsed permissions
EXTERNAL_IMPORTConnectorImport data with the connector user.
INTERNAL_ENRICHMENTConnectorEnrich data with the connector user.
INTERNAL_IMPORT_FILEConnectorImport data with the connector user.
INTERNAL_EXPORT_FILEAdministratorExport data with the user who requested the export.
STREAMConnectorConsume the streams with the connector user.
+
+

Parameters

+

In addition to these 2 parameters, connectors have other mandatory parameters that need to be set in order to get them work.

+

Here is an example of a connector docker-compose.yml file: +

- CONNECTOR_ID=ChangeMe
+- CONNECTOR_TYPE=EXTERNAL_IMPORT
+- CONNECTOR_NAME=MITRE ATT&CK
+- CONNECTOR_SCOPE=identity,attack-pattern,course-of-action,intrusion-set,malware,tool,report
+- CONNECTOR_LOG_LEVEL=info
+

+

Here is an example in a connector config.yml file:

+
connector:
+  id: 'ChangeMe'
+  type: 'EXTERNAL_IMPORT'
+  name: 'MITRE ATT&CK'
+  scope: 'identity,attack-pattern,course-of-action,intrusion-set,malware,tool,report'
+  log_level: 'info'
+
+

Advanced parameters

+

By default, connectors are connecting to RabbitMQ using parameters and credentials directly given by the API during the connector registration process. In some cases, you may need to override them.

+
- MQ_HOST=rabbit.mydomain.com
+- MQ_PORT=5672
+- MQ_VHOST=/
+- MQ_USE_SSL=false
+- MQ_USER=guest
+- MQ_PASS=guest
+
+

Here is an example in a connector config.yml file:

+
mq:
+  host: 'rabbit.mydomain.com'
+  port: '5672'
+  use_ssl: false
+  user: 'guest'
+  pass: 'guest'
+
+

Networking

+

Be aware that all connectors are reaching RabbitMQ based the RabbitMQ configuration provided by the OpenCTI platform. The connector must be able to reach RabbitMQ on the specified hostname and port. If you have a specific Docker network configuration, please be sure to adapt your docker-compose.yml file in such way that the connector container gets attached to the OpenCTI Network, e.g.:

+
networks:
+  default:
+    external: true
+    name: opencti-docker_default
+
+

+

Connector token

+

Create the user

+

As mentioned previously, it is strongly recommended to run each connector with its own user. The Internal Export File connectors should be launched with a user that belongs to a group which has an “Administrator” role (with bypass all capabilities enabled).

+

By default, in platform, a group named "Connectors" already exists. So just create a new user with the name [C] Name of the connector in Settings > Security > Users.

+

Create user

+

Put the user in the group

+

Just go to the user you have just created and add it to the Connectors group.

+

User groups

+

Then just get the token of the user displayed in the interface.

+

User token

+

Docker activation

+

You can either directly run the Docker image of connectors or add them to your current docker-compose.yml file.

+

Add a connector to your deployment

+

For instance, to enable the MISP connector, you can add a new service to your docker-compose.yml file:

+
  connector-misp:
+    image: opencti/connector-misp:latest
+    environment:
+      - OPENCTI_URL=http://localhost
+      - OPENCTI_TOKEN=ChangeMe
+      - CONNECTOR_ID=ChangeMe
+      - CONNECTOR_TYPE=EXTERNAL_IMPORT
+      - CONNECTOR_NAME=MISP
+      - CONNECTOR_SCOPE=misp
+      - CONNECTOR_LOG_LEVEL=info
+      - MISP_URL=http://localhost # Required
+      - MISP_KEY=ChangeMe # Required
+      - MISP_SSL_VERIFY=False # Required
+      - MISP_CREATE_REPORTS=True # Required, create report for MISP event
+      - MISP_REPORT_CLASS=MISP event # Optional, report_class if creating report for event
+      - MISP_IMPORT_FROM_DATE=2000-01-01 # Optional, import all event from this date
+      - MISP_IMPORT_TAGS=opencti:import,type:osint # Optional, list of tags used for import events
+      - MISP_INTERVAL=1 # Required, in minutes
+    restart: always
+
+

Launch a standalone connector

+

To launch a standalone connector, you can use the docker-compose.yml file of the connector itself. Just download the latest release and start the connector:

+
$ wget https://github.com/OpenCTI-Platform/connectors/archive/{RELEASE_VERSION}.zip
+$ unzip {RELEASE_VERSION}.zip
+$ cd connectors-{RELEASE_VERSION}/misp/
+
+

Change the configuration in the docker-compose.yml according to the parameters of the platform and of the targeted service. Then launch the connector:

+
$ docker-compose up
+
+

Manual activation

+

If you want to manually launch connector, you just have to install Python 3 and pip3 for dependencies:

+
$ apt install python3 python3-pip
+
+

Download the release of the connectors:

+
$ wget <https://github.com/OpenCTI-Platform/connectors/archive/{RELEASE_VERSION}.zip>
+$ unzip {RELEASE_VERSION}.zip
+$ cd connectors-{RELEASE_VERSION}/misp/src/
+
+

Install dependencies and initialize the configuration:

+
$ pip3 install -r requirements.txt
+$ cp config.yml.sample config.yml
+
+

Change the config.yml content according to the parameters of the platform and of the targeted service and launch the connector:

+
$ python3 misp.py
+
+

Connectors status

+

The connector status can be displayed in the dedicated section of the platform available in Data > Ingestion > Connectors. You will be able to see the statistics of the RabbitMQ queue of the connector:

+

Connectors

+
+

Problem

+

If you encounter problems deploying OpenCTI or connectors, you can consult the troubleshooting page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/installation/index.html b/6.2.X/deployment/installation/index.html new file mode 100755 index 00000000..5cf580a1 --- /dev/null +++ b/6.2.X/deployment/installation/index.html @@ -0,0 +1,5942 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Installation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Installation

+

All components of OpenCTI are shipped both as Docker images and manual installation packages.

+
+

Production deployment

+

For production deployment, we recommend to deploy all components in containers, including dependencies, using native cloud services or orchestration systems such as Kubernetes.

+

To have more details about deploying OpenCTI and its dependencies in cluster mode, please read the dedicated section.

+
+
+
    +
  • +

    Use Docker

    +
    +

    Deploy OpenCTI using Docker and the default docker-compose.yml provided +in the docker.

    +

    Setup

    +
  • +
  • +

    Manual installation

    +
    +

    Deploy dependencies and launch the platform manually using the packages +released in the GitHub releases.

    +

    Explore

    +
  • +
+
+

Using Docker

+

OpenCTI can be deployed using the docker-compose command.

+
+

Deploy FIPS 140-2 compliant components

+

We provide FIPS 140-2 compliant images. Please read the dedicated documentation to understand how to deploy OpenCTI in FIPS-compliant mode.

+
+

Pre-requisites

+

Linux

+
sudo apt install docker-compose
+
+

Windows and MacOS

+

Just download the appropriate Docker for Desktop version for your operating system.

+

Clone the repository

+

Docker helpers are available in the Docker GitHub repository.

+
mkdir -p /path/to/your/app && cd /path/to/your/app
+git clone https://github.com/OpenCTI-Platform/docker.git
+cd docker
+
+

Configure the environment

+
+

ElasticSearch / OpenSearch configuration

+
    +
  • We strongly recommend that you add the following ElasticSearch / OpenSearch parameter:
  • +
+
thread_pool.search.queue_size=5000
+
+ +
+

Before running the docker-compose command, the docker-compose.yml file should be configured. By default, the docker-compose.yml file is using environment variables available in the file .env.sample.

+

You can either rename the file .env.sample as .env and enter the values or just directly edit the docker-compose.yml with the values for your environment.

+
+

Configuration static parameters

+

The complete list of available static parameters is available in the configuration section.

+
+

Here is an example to quickly generate the .env file under Linux, especially all the default UUIDv4:

+
sudo apt install -y jq
+cd ~/docker
+(cat << EOF
+OPENCTI_ADMIN_EMAIL=admin@opencti.io
+OPENCTI_ADMIN_PASSWORD=ChangeMePlease
+OPENCTI_ADMIN_TOKEN=$(cat /proc/sys/kernel/random/uuid)
+OPENCTI_BASE_URL=http://localhost:8080
+MINIO_ROOT_USER=$(cat /proc/sys/kernel/random/uuid)
+MINIO_ROOT_PASSWORD=$(cat /proc/sys/kernel/random/uuid)
+RABBITMQ_DEFAULT_USER=guest
+RABBITMQ_DEFAULT_PASS=guest
+ELASTIC_MEMORY_SIZE=4G
+CONNECTOR_HISTORY_ID=$(cat /proc/sys/kernel/random/uuid)
+CONNECTOR_EXPORT_FILE_STIX_ID=$(cat /proc/sys/kernel/random/uuid)
+CONNECTOR_EXPORT_FILE_CSV_ID=$(cat /proc/sys/kernel/random/uuid)
+CONNECTOR_IMPORT_FILE_STIX_ID=$(cat /proc/sys/kernel/random/uuid)
+CONNECTOR_EXPORT_FILE_TXT_ID=$(cat /proc/sys/kernel/random/uuid)
+CONNECTOR_IMPORT_DOCUMENT_ID=$(cat /proc/sys/kernel/random/uuid)
+SMTP_HOSTNAME=localhost
+EOF
+) > .env
+
+

If your docker-compose deployment does not support .env files, just export all environment variables before launching the platform:

+
export $(cat .env | grep -v "#" | xargs)
+
+

As OpenCTI has a dependency on ElasticSearch, you have to set vm.max_map_count before running the containers, as mentioned in the ElasticSearch documentation.

+
sudo sysctl -w vm.max_map_count=1048575
+
+

To make this parameter persistent, add the following to the end of your /etc/sysctl.conf:

+
vm.max_map_count=1048575
+
+

Persist data

+

The default for OpenCTI data is to be persistent.

+

In docker-compose.yml, you will find the list of necessary persistent volumes for the dependencies at the end:

+
volumes:
+  esdata:     # ElasticSearch data
+  s3data:     # S3 bucket data
+  redisdata:  # Redis data
+  amqpdata:   # RabbitMQ data
+
+

Run OpenCTI

+

Using single node Docker

+

After changing your .env file run docker-compose in detached (-d) mode:

+
sudo systemctl start docker.service
+# Run docker-compose in detached
+docker-compose up -d
+
+

Using Docker swarm

+

In order to have the best experience with Docker, we recommend using the Docker stack feature. In this mode you will have the capacity to easily scale your deployment.

+
# If your virtual machine is not a part of a Swarm cluster, please use:
+docker swarm init
+
+

Put your environment variables in /etc/environment:

+
# If you already exported your variables to .env from above:
+sudo cat .env >> /etc/environment
+sudo bash -c 'cat .env >> /etc/environment'
+sudo docker stack deploy --compose-file docker-compose.yml opencti
+
+
+

Installation done

+

You can now go to http://localhost:8080 and log in with the credentials configured in your environment variables.

+
+

Manual installation

+

Prerequisites

+

Installation of dependencies

+

You have to install all the needed dependencies for the main application and the workers. The example below is for Debian-based systems:

+
sudo apt-get update
+sudo apt-get install build-essential nodejs npm python3 python3-pip python3-dev
+
+

Download the application files

+

First, you have to download and extract the latest release file. Then select the version to install depending of your operating system:

+

For Linux:

+
    +
  • If your OS supports libc (Ubuntu, Debian, ...) you have to install the opencti-release_{RELEASE_VERSION}.tar.gz version.
  • +
  • If your OS uses musl (Alpine, ...) you have to install the opencti-release-{RELEASE_VERSION}_musl.tar.gz version.
  • +
+

For Windows:

+

We don't provide any Windows release for now. However it is still possible to check the code out, manually install the dependencies and build the software.

+
mkdir /path/to/your/app && cd /path/to/your/app
+wget <https://github.com/OpenCTI-Platform/opencti/releases/download/{RELEASE_VERSION}/opencti-release-{RELEASE_VERSION}.tar.gz>
+tar xvfz opencti-release-{RELEASE_VERSION}.tar.gz
+
+

Install the main platform

+

Configure the application

+

The main application has just one JSON configuration file to change and a few Python modules to install

+
cd opencti
+cp config/default.json config/production.json
+
+

Change the config/production.json file according to your configuration of ElasticSearch, Redis, RabbitMQ and S3 bucket as well as default credentials (the ADMIN_TOKEN must be a valid UUID).

+

Install the Python modules

+
cd src/python
+pip3 install -r requirements.txt
+cd ../..
+
+

Start the application

+

The application is just a NodeJS process, the creation of the database schema and the migration will be done at starting.

+
+

Please verify that yarn version is greater than 4 and node version is greater or equals to v19. +Please note that some Node.js version are outdated in linux package manager, you can download a recent one in https://nodejs.org/en/download or alternatively nvm can help to chose a recent version of Node.js https://github.com/nvm-sh/nvm

+
+
yarn --version
+#4.1.0
+node --version
+#v20.11.1
+
+

Once Node.js is setup, you can build and run with (from inside opencti folder): +

yarn install
+yarn build
+yarn serv
+

+

The default username and password are those you have put in the config/production.json file.

+

Install the worker

+

The OpenCTI worker is used to write the data coming from the RabbitMQ messages broker.

+

Configure the worker

+
cd worker
+pip3 install -r requirements.txt
+cp config.yml.sample config.yml
+
+

Change the config.yml file according to your OpenCTI token.

+

Start as many workers as you need

+
python3 worker.py &
+python3 worker.py &
+
+
+

Installation done

+

You can now go to http://localhost:4000 and log in with the credentials configured in your production.json file.

+
+

Community contributions

+

Terraform

+
+
    +
  • +

    Multi-clouds Terraform scripts

    +
    +

    This repository is here to provide you with a quick and easy way to deploy an OpenCTI instance in the cloud (AWS, Azure, or GCP).

    +

    GitHub Respository

    +
  • +
  • +

    AWS Advanced Terraform scripts

    +
    +

    A Terraform deployment of OpenCTI designed to make use of native AWS Resources (where feasible). This includes AWS ECS Fargate, AWS OpenSearch, etc.

    +

    GitHub Repository

    +
  • +
+
+

Helm Charts

+
+ +
+

Deploy behind a reverse proxy

+

If you want to use OpenCTI behind a reverse proxy with a context path, like https://domain.com/opencti, please change the base_path static parameter.

+
    +
  • APP__BASE_PATH=/opencti
  • +
+

By default OpenCTI use websockets so don't forget to configure your proxy for this usage, an example with Nginx:

+
location / {
+    proxy_cache                 off;
+    proxy_buffering             off;
+    proxy_http_version          1.1;
+    proxy_set_header Upgrade    $http_upgrade;
+    proxy_set_header Connection "upgrade";
+    proxy_set_header Host       $host;
+    chunked_transfer_encoding   off;
+    proxy_pass                  http://YOUR_UPSTREAM_BACKEND;
+  }
+
+

Additional memory information

+

Platform

+

OpenCTI platform is based on a NodeJS runtime, with a memory limit of 8GB by default. If you encounter OutOfMemory exceptions, this limit could be changed:

+
- NODE_OPTIONS=--max-old-space-size=8096
+
+

Workers and connectors

+

OpenCTI workers and connectors are Python processes. If you want to limit the memory of the process, we recommend to directly use Docker to do that. You can find more information in the official Docker documentation.

+

ElasticSearch

+

ElasticSearch is also a JAVA process. In order to setup the JAVA memory allocation, you can use the environment variable ES_JAVA_OPTS. You can find more information in the official ElasticSearch documentation.

+

Redis

+

Redis has a very small footprint on keys but will consume memory for the stream. By default the size of the stream is limited to 2 millions which represents a memory footprint around 8 GB. You can find more information in the Redis docker hub.

+

MinIO / S3 Bucket

+

MinIO is a small process and does not require a high amount of memory. More information are available for Linux here on the Kernel tuning guide.

+

RabbitMQ

+

The RabbitMQ memory configuration can be find in the RabbitMQ official documentation. RabbitMQ will consumed memory until a specific threshold, therefore it should be configure along with the Docker memory limitation.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/integrations/index.html b/6.2.X/deployment/integrations/index.html new file mode 100755 index 00000000..e927b94f --- /dev/null +++ b/6.2.X/deployment/integrations/index.html @@ -0,0 +1,5309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Integrations - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Integrations

+

Introduction

+

OpenCTI supports multiple ways to integrate with other systems which do not have native connectors or plugins to the platform. Here are the technical features available to ease the connection and the integration of the platform with other applications.

+
+

Connectors list

+

If you are looking for the list of OpenCTI connectors or native integration, please check the OpenCTI Ecosystem.

+
+

Native feeds and streams

+

To ease integrations with other products, OpenCTI has built-in capabilities to deliver the data to third-parties.

+

CSV Feeds

+

It is possible to create as many CSV feeds as needed, based on filters and accessible in HTTP. CSV feeds are available in Data > Data sharing > CSV deeds.

+

When creating a CSV feed, you need to select one or multiple types of entities to make available. Then, you must assign a field (of an entity type) to each column in the CSV:

+

CSV Feeds

+
+

Details

+

For more information about CSV feeds, filters and configuration, please check the Native feeds page.

+
+

TAXII collections

+

Most of the modern cybersecurity systems such as SIEMs, EDRs, XDRs and even firewalls support the TAXII protocol which is basically a paginated HTTP STIX feed. OpenCTI implements a TAXII 2.1 server with the ability to create as many TAXII collections as needed in Data > Data sharing > TAXII Collections.

+

TAXII collections are a sub-selection of the knowledge available in the platform and rely on filters. For instance, it is possible to create TAXII collections for pieces of malware with a given label, for indicators with a score greater than n, etc.

+

TAXII Feeds

+

Live Streams

+

After implementing CSV feeds and TAXII collections, we figured out that those 2 stateless APIs are definitely not enough when it comes to tackle advanced information sharing challenges such as:

+
    +
  • Real time transmission of the information (i.e. avoid hundreds of systems to pull data every 5 minutes).
  • +
  • Dependencies resolution (i.e. an intrusion created by an organization but the organization is not in the TAXII collection).
  • +
  • Partial update for huge entities such as report (i.e. just having the update event).
  • +
  • Delete events when necessary (i.e. to handle indicators expiration in third party systems for instance).
  • +
+

That's why we've developed the live streams. They are available in Data > Data sharing > Live streams. As with TAXII collections, it is possible to create as many streams as needed using filters.

+

Streams

+

Streams implement the HTTP SSE (Server-sent events) protocol and give applications the possibility to consume a real time pure STIX 2.1 stream. Stream connectors in the OpenCTI Ecosystem are using live streams to consume data and do something such as create / update / delete information in SIEMs, XDRs, etc.

+

Authentication

+

For all previously explained capabilities, as they are over the HTTP protocol, 3 authentication mechanisms are available to consume them.

+
    +
  1. +

    Using a bearer header with your OpenCTI API key

    +
    Authorization: Bearer a17bc103-8420-4208-bd53-e1f80845d15f
    +
    +
    +

    API Key

    +

    Your API key can be found in your profile available clicking on the top right icon.

    +
    +
  2. +
  3. +

    Using basic authentication

    +
    Username: Your platform username
    +Password: Your plafrom password
    +Authorization: Basic c2FtdWVsLmhhc3NpbmVBZmlsaWdyYW4uaW86TG91aXNlMTMwNCM=
    +
    +
  4. +
  5. +

    Using client certificate authentication

    +

    To know how to configure the client certificate authentication, please consult the authentication configuration section.

    +
  6. +
+

API and libraries

+

GraphQL API

+

To allow analysts and developers to implement more custom or complex use cases, a full GraphQL API is available in the application on the /graphql endpoint.

+

The API can be queried using various GraphQL client such as Postman but you can leverage any HTTP client to forge GraphQL queries using POST methods.

+

Authentication

+

The API authentication can be performed using the token of a user and a classic Authorization header:

+
Content-Type: application/json
+Authorization: Bearer 6b6554c4-bb2c-4c80-9cd3-30288c8bf424
+
+

Playground

+

The playground is available on the /graphql endpoint. A link button is also available in the profile of your user.

+

Playground

+

All the schema documentation is directly available in the playground.

+

Schema

+

If you already logged to OpenCTI with the same browser you should be able to directly do some requests. If you are not authenticated or want to authenticate only through the playground you can use a header configuration using your profile token

+

Example of configuration (bottom left of the playground):

+

Playground authentication

+
+

Additional GraphQL documentation

+

To find out more about GraphQL and the playground, you can find two additional documentation pages: the GraphQL API page and the GraphQL playground page.

+
+

Python library

+

Since not everyone is familiar with GraphQL APIs, we've developed a Python library to ease the interaction with it. The library is pretty easy to use. To initiate the client:

+
# coding: utf-8
+
+from pycti import OpenCTIApiClient
+
+# Variables
+api_url = "http://opencti:4000"
+api_token = "bfa014e0-e02e-4aa6-a42b-603b19dcf159"
+
+# OpenCTI initialization
+opencti_api_client = OpenCTIApiClient(api_url, api_token)
+
+

Then just use the available helpers: +

# Search for malware with the keyword "windows"
+malwares = opencti_api_client.malware.list(search="windows")
+
+# Print
+print(malwares)
+

+
+

Details

+

For more detailed information about the Python library, please read the dedicated section.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/managers/index.html b/6.2.X/deployment/managers/index.html new file mode 100755 index 00000000..a7f14ccb --- /dev/null +++ b/6.2.X/deployment/managers/index.html @@ -0,0 +1,5290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Platform managers - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Platform managers

+

Platform managers are background components that perform various tasks to support some important functionalities in the platform.

+

Here is a list of all the managers on the platform:

+

Rules engine

+

Allows users to execute pre-defined actions based on the data and events in the platform.

+

These rules are accessible in Settings > Customization > Rules engine.

+

The rules engine is designed to help users automate and streamline their cyber threat intelligence processes.

+

More information can be found here.

+

History manager

+

This manager keeps tracks of user/connector interactions on entities in the platform.

+

It is designed to help users audit and understand the evolution of their CTI data.

+

Activity manager

+

The activity manager in OpenCTI is a component that monitors and logs the user actions in the platform such as login, settings update, and user activities if configured (read, udpate, etc.).

+

More information can be found here.

+

Background task manager

+

Is a component that handles the execution of tasks, such as importing data, exporting data and mass operations.

+

More information can be found here.

+

Expiration scheduler

+

The expiration scheduler is responsible for monitoring expired elements in the platform. +It cancels the access rights of expired user accounts and revokes expired indicators from the platform.

+

Synchronization manager

+

The synchronization manager enables the data sharing between multiple OpenCTI platforms. +It allows the user to create and configure synchronizers which are processes that connect to the live streams of remote OpenCTI platforms and import the data into the local platform.

+

Retention manager

+

The retention manager is a component that allows the user to define rules to help delete data in OpenCTI that is no longer relevant or useful. This helps to optimize the performance and storage of the OpenCTI platform and ensures the quality and accuracy of the data.

+

More information can be found here.

+

Notification manager

+

The notification manager is a component that allows the user to customize and receive alerts about events/changes in the platform.

+

More information can be found here.

+

Ingestion manager

+

The ingestion manager in OpenCTI is a component that manages the ingestion of data from RSS, TAXII and CSV feeds.

+

Playbook manager

+

The playbook manager handles the automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform.

+

Please read the Playbook automation page to get more information.

+

File index manager

+

The file indexing manager extracts and indexes the text content of the files, and stores it in the database. +It allows users to search for text content within files uploaded to the platform.

+

More information can be found here.

+

Indicator decay manager

+

The indicator decay manager allows to update indicators score automatically based on configured decay rules.

+

More information can be found here.

+

Trash manager

+

The trash manager is responsible to delete permanently elements stored in the trash after a specified period of time (7 days by default).

+

Filigran telemetry manager

+

The telemetry manager collects periodically statistical data about platform usage.

+

More information about data telemetry can be found here.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/map/index.html b/6.2.X/deployment/map/index.html new file mode 100755 index 00000000..abb03619 --- /dev/null +++ b/6.2.X/deployment/map/index.html @@ -0,0 +1,5091 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + On-premise map server - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Deploy on-premise map server with OpenCTI styles

+

Introduction

+

The OpenStreetMap tiles for the planet will take 80GB. Here are the instructions to deploy a local OpenStreetMap server with the OpenCTI styles.

+

Create directory for the data and upload planet data

+

When you will launch the map server container, it will be necessary to mount a volume with the planet tiles data. Just create the directory for the data.

+
mkdir /var/YOUR_DATA_DIR
+
+

We have hosted the free-to-use planet tiles, just download the planet data from filigran.io.

+
wget https://filigran.io/app/uploads/maptiler-osm-2020-02-10-v3.11-planet.mbtiles
+
+

Put the file maptiler-osm-2020-12-14-v3.11-planet.mbtiles in the data directory:

+
cp maptiler-osm-2020-12-14-v3.11-planet.mbtiles /var/YOUR_DATA_DIR/
+
+

Start Docker

+

Replace the port 80 by the port you would like to expose. Inside the Docker, the map server listen on the port 80.

+
docker run -d --restart always -v /var/YOUR_DATA_DIR:/data -p 80:80 klokantech/openmaptiles-server:latest 
+
+

Configure

+

Download on your computer the OpenCTI JSON styles (you will have to upload it through the web UI later).

+ +

Now, you can access to the map server, you should see the following page:

+

Install

+

On the next page, you should see the existing data:

+

Region

+

On the next page, click on "Advanced Options":

+

Options

+

Upload the filigran-dark2.json and filigran-light2.json files:

+

Styles

+

Save and run the server with default parameters:

+

Settings

+

OpenCTI Parameters

+

Once the server is running, you should see the list of available styles:

+

Viewer

+

Click on "Viewer", and take the URL:

+
+

👉 http:/YOUR_URL/styles/{ID}/....

+
+

In the OpenCTI configuration, just put:

+ + + + + + + + + + + + + + + + + + + + + + + +
ParameterEnvironment variableValueDescription
app:map_tile_server_darkAPP__MAP_TILE_SERVER_DARKhttp://{YOUR_MAP_SERVER}/styles/{ID_DARK}/{z}/{x}/{y}.pngThe address of the OpenStreetMap provider with dark theme style
app:map_tile_server_lightAPP__MAP_TILE_SERVER_LIGHThttp://{YOUR_MAP_SERVER}/styles/{ID_LIGHT}/{z}/{x}/{y}.pngThe address of the OpenStreetMap provider with light theme style
+

You're good to go!

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/overview/index.html b/6.2.X/deployment/overview/index.html new file mode 100755 index 00000000..96c0dab2 --- /dev/null +++ b/6.2.X/deployment/overview/index.html @@ -0,0 +1,5251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Overview

+

Before starting the installation, let's discover how OpenCTI is working, which dependencies are needed and what are the minimal requirements to deploy it in production.

+

Architecture

+

The OpenCTI platform relies on several external databases and services in order to work.

+

Architecture

+

Platform

+

The platform is the central part of the OpenCTI technological stack. It allows users to access to the user interface but also provides the GraphQL API used by connectors and workers to insert data. In the context of a production deployment, you may need to scale horizontally and launch multiple platforms behind a load balancer connected to the same databases (ElasticSearch, Redis, S3, RabbitMQ).

+

Workers

+

The workers are standalone Python processes consuming messages from the RabbitMQ broker in order to do asynchronous write queries. You can launch as many workers as you need to increase the write performances. At some point, the write performances will be limited by the throughput of the ElasticSearch database cluster.

+
+

Number of workers

+

If you need to increase performances, it is better to launch more platforms to handle worker queries. The recommended setup is to have at least one platform for 3 workers (ie. 9 workers distributed over 3 platforms).

+
+

Connectors

+

The connectors are third-party pieces of software (Python processes) that can play five different +roles on the platform:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionExamples
EXTERNAL_IMPORTPull data from remote sources, convert it to STIX2 and insert it on the OpenCTI platform.MITRE Datasets, MISP, CVE, AlienVault, Mandiant, etc.
INTERNAL_ENRICHMENTListen for new OpenCTI entities or users requests, pull data from remote sources to enrich.Shodan, DomainTools, IpInfo, etc.
INTERNAL_IMPORT_FILEExtract data from files uploaded on OpenCTI through the UI or the API.STIX 2.1, PDF, Text, HTML, etc.
INTERNAL_EXPORT_FILEGenerate export from OpenCTI data, based on a single object or a list.STIX 2.1, CSV, PDF, etc.
STREAMConsume a platform data stream and do something with events.Splunk, Elastic Security, Q-Radar, etc.
+
+

List of connectors

+

You can find all currently available connectors in the OpenCTI Ecosystem.

+
+

Infrastructure requirements

+

Dependencies

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentVersionCPURAMDisk typeDisk space
ElasticSearch / OpenSearch>= 8.0 / >= 2.92 cores≥ 8GBSSD≥ 16GB
Redis>= 7.11 core≥ 1GBSSD≥ 16GB
RabbitMQ>= 3.111 core≥ 512MBStandard≥ 2GB
S3 / MinIO>= RELEASE.2023-021 core≥ 128MBSSD≥ 16GB
+

Platform

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentCPURAMDisk typeDisk space
OpenCTI Core2 cores≥ 8GBNone (stateless)-
Worker(s)1 core≥ 128MBNone (stateless)-
Connector(s)1 core≥ 128MBNone (stateless)-
+
+

Clustering

+

To have more details about deploying OpenCTI and its dependencies in cluster mode, please read the dedicated section.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/resources/index.html b/6.2.X/deployment/resources/index.html new file mode 100755 index 00000000..9998ca4e --- /dev/null +++ b/6.2.X/deployment/resources/index.html @@ -0,0 +1,5073 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Other resources - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Other resources

+

Introduction

+

OpenCTI is an open and modular platform. A lot of connectors, plugins and clients are created by Filigran and by the community. You can find here other resources available to complete your OpenCTI journey.

+

Videos & training

+
+
    +
  • +

    YouTube channel

    +
    +

    Watch demonstration videos, use case explanations, customers and community +testimonies, and past webinars.

    +

    Watch

    +
  • +
  • +

    Training courses

    +
    +

    Empower your journey with OpenCTI training courses for both analyst and +administrators, and get your certificate.

    +

    Learn

    +
  • +
+
+

Articles & news

+
+
    +
  • +

    Blog articles

    +
    +

    Read posts written by both Filigran teams and community members about OpenCTI +features and use cases.

    +

    Read

    +
  • +
  • +

    Newsletters

    +
    +

    Subscribe to Filigran newsletters to get informed about the latest evolutions +of our product ecosystems.

    +

    Subscribe

    +
  • +
+
+

Analysis

+
+
    +
  • +

    Verticalized threat landscapes

    +
    +

    Access to monthly sectorial analysis from our experts team based on knowledge and +data collected by our partners.

    +

    Consult

    +
  • +
  • +

    Case studies

    +
    +

    Explore the Filigran case studies about stories and usages of the platform +among our communities and customers.

    +

    Download

    +
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/rollover/index.html b/6.2.X/deployment/rollover/index.html new file mode 100755 index 00000000..41500dc4 --- /dev/null +++ b/6.2.X/deployment/rollover/index.html @@ -0,0 +1,5453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Indices and rollover - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Indices and rollover policies

+
+

Default rollover policies

+

Since OpenCTI 5.9.0, rollover policies are automatically created when the platform is initialized for the first time. If your platform has been initialized using an older version of OpenCTI or if you would like to understand (and customize) rollover policies please read the following documentation.

+
+

Introduction

+

ElasticSearch and OpenSearch both support rollover on indices. OpenCTI has been designed to be able to use aliases for indices and so supports index lifecycle policies very well. Thus, by default OpenCTI initializes indices with a suffix of -00001 and uses wildcards to query indices. When rollover policies are implemented (default starting OCTI 5.9.X if you initialized your platform at this version), indices are splitted to keep a reasonable volume of data in shards.

+

Indices

+

OpenCTI Integration User Permissions in OpenSearch/ElasticSearch

+
    +
  • +

    Index Permissions

    + +
  • +
  • +

    Cluster Permissions

    +
      +
    • cluster_composite_ops_ro
    • +
    • cluster_manage_index_templates
    • +
    • cluster:admin/ingest/pipeline/put
    • +
    • cluster:admin/opendistro/ism/policy/write
    • +
    • cluster:monitor/health
    • +
    • cluster:monitor/main
    • +
    • cluster:monitor/state
    • +
    • indices:admin/index_template/put
    • +
    • indices:data/read/scroll/clear
    • +
    • indices:data/read/scroll
    • +
    • indices:data/write/bulk
    • +
    +
  • +
+
+

About indices:* in Cluster Permissions

+

It is crucial to include indices:* permissions in Cluster Permissions for the proper functioning of the OpenCTI integration. Removing these, even if already present in Index Permissions, may result in startup issues for the OpenCTI Platform.

+
+

ElasticSearch configuration

+

Indices

+

By default, a rollover policy is applied on all indices used by OpenCTI.

+
    +
  • opencti_deleted_objects
  • +
  • opencti_files
  • +
  • opencti_history
  • +
  • opencti_inferred_entities
  • +
  • opencti_inferred_relationships
  • +
  • opencti_internal_objects
  • +
  • opencti_internal_relationships
  • +
  • opencti_stix_core_relationships
  • +
  • opencti_stix_cyber_observable_relationships
  • +
  • opencti_stix_cyber_observables
  • +
  • opencti_stix_domain_objects
  • +
  • opencti_stix_meta_objects
  • +
  • opencti_stix_meta_relationships
  • +
  • opencti_stix_sighting_relationships
  • +
+

For your information, the indices which can grow rapidly are:

+
    +
  • Index opencti_stix_meta_relationships: it contains all the nested relationships between objects and labels / marking definitions / external references / authors, etc.
  • +
  • Index opencti_history: it contains the history log of all objects in the platform.
  • +
  • Index opencti_stix_cyber_observables: it contains all observables stored in the platform.
  • +
  • Index opencti_stix_core_relationships: it contains all main STIX relationships stored in the platform.
  • +
+

Default implemented lifecycle policy

+

Here is the recommended policy (initialized starting 5.9.X):

+
    +
  • Maximum primary shard size: 50 GB
  • +
  • Maximum age: 365 days
  • +
  • Maximum documents: 75,000,000
  • +
+

Applying rollover policies on existing indices

+
+

Procedure information

+

Please read the following only if your platform has been initialized before 5.9.0, otherwise lifecycle policies has been created (but you can still cutomize them).

+
+

Unfortunately, to be able to implement rollover policies on ElasticSearch / OpenSearch indices, it will be needed to re-index all the data in new indices using ElasticSearch capabilities.

+

Shutdown

+

First step is to shutdown your OpenCTI platform.

+

Change configuration

+

Then, in the OpenCTI configuration, change the ElasticSearch / OpenSearch default prefix to octi (default is opencti).

+

Create the rollover policy

+

Create a rollover policy named octi-ilm-policy (in Kibana, Management > Index Lifecycle Policies):

+
    +
  • Maximum primary shard size: 50 GB
  • +
  • Maximum age: 365 days
  • +
  • Maximum documents: 75,000,000
  • +
+

ILM Policy

+

Create index templates

+

In Kibana, clone the opencti-index-template to have one index template by OpenCTI index with the appropriate rollover policy, index pattern and rollover alias (in Kibana, Management > Index Management > Index Templates).

+

Index template

+

Create the following index templates:

+
    +
  • octi_deleted_objects
  • +
  • octi_files
  • +
  • octi_history
  • +
  • octi_inferred_entities
  • +
  • octi_inferred_relationships
  • +
  • octi_internal_objects
  • +
  • octi_internal_relationships
  • +
  • octi_stix_core_relationships
  • +
  • octi_stix_cyber_observable_relationships
  • +
  • octi_stix_cyber_observables
  • +
  • octi_stix_domain_objects
  • +
  • octi_stix_meta_objects
  • +
  • octi_stix_meta_relationships
  • +
  • octi_stix_sighting_relationships
  • +
+

Here is the overview of all templates (you should have something with octi_ instead of opencti_).

+

Index templates

+

Apply rollover policy on all index templates

+

Then, going back in the index lifecycle policies screen, you can click on the "+" button of the octi-ilm-policy to Add the policy to index template, then add the policy to add previously created template with the proper "Alias for rollover index".

+

Add policy to template

+

Bootstrap all new indices

+

Before we can re-index, we need to create the new indices with aliases.

+
PUT octi_history-000001
+{
+  "aliases": {
+    "octi_history": {
+      "is_write_index": true
+    }
+  }
+}
+
+

Repeat this step for all indices:

+
    +
  • octi_deleted_objects
  • +
  • octi_files
  • +
  • octi_history
  • +
  • octi_inferred_entities
  • +
  • octi_inferred_relationships
  • +
  • octi_internal_objects
  • +
  • octi_internal_relationships
  • +
  • octi_stix_core_relationships
  • +
  • octi_stix_cyber_observable_relationships
  • +
  • octi_stix_cyber_observables
  • +
  • octi_stix_domain_objects
  • +
  • octi_stix_meta_objects
  • +
  • octi_stix_meta_relationships
  • +
+

Re-index all indices

+

Using the reindex API, re-index all indices one by one:

+
curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
+{
+  "source": {
+    "index": "opencti_history-000001"
+  },
+  "dest": {
+    "index": "octi_history"
+  }
+}
+'
+
+

You will see the rollover policy to be applied and the new indices are automatically rolled-over during re-indexation.

+

Delete all old indices

+

Then just delete all indices with the prefix opencti_.

+

Start your platform

+

Start your platform, using the new indices.

+
+

Rollover documentation

+

To have more details about automatic rollover and lifecycle policies, please read the official ElasticSearch documentation.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/troubleshooting/index.html b/6.2.X/deployment/troubleshooting/index.html new file mode 100755 index 00000000..5ca525ec --- /dev/null +++ b/6.2.X/deployment/troubleshooting/index.html @@ -0,0 +1,5201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Troubleshooting - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Troubleshooting

+

This page aims to explain the typical errors you can have with your OpenCTI platform.

+

Finding the relevant logs

+

It is highly recommended to monitor the error logs of the platforms, workers and connectors. All the components have log outputs in an understandable JSON format. If necessary, it is always possible to increase the log level. In production, it is recommended to have the log level set to error.

+

Platform

+

Here are some useful parameters for platform logging:

+
- APP__APP_LOGS__LOGS_LEVEL=[error|warning|info|debug]
+- APP__APP_LOGS__LOGS_CONSOLE=true # Output in the container console
+
+

Connectors

+

All connectors support the same set of parameters to manage the log level and outputs:

+
- OPENCTI_JSON_LOGGING=true # Enable / disable JSON logging
+- CONNECTOR_LOG_LEVEL=info=[error|warning|info|debug]
+
+

Workers

+

The workers can have more or less verbose outputs:

+
- OPENCTI_JSON_LOGGING=true # Enable / disable JSON logging
+- WORKER_LOG_LEVEL=[error|warning|info|debug]
+
+

ElasticSearch / OpenSearch data

+
+

Kibana / OpenSearch dashboard

+

In case you need to troubleshoot the OpenCTI knowledge data, we recommend to install Kibana or OpenSearch dashboard.

+
+

Common errors

+

Ingestion technical errors

+
+

Missing reference to handle creation

+

After 5 retries, if an element required to create another element is missing, the platform raises an exception. It usually comes from a connector that generates inconsistent STIX 2.1 bundles.

+
+
+

Cant upsert entity. Too many entities resolved

+

OpenCTI received an entity which is matching too many other entities in the platform. In this condition we cannot take a decision. We need to dig into the data bundle to identify why it matches too much entities and fix the data in the bundle / or the platform according to what you expect.

+
+
+

Execution timeout, too many concurrent call on the same entities

+

The platform supports multi workers and multiple parallel creation but different parameters can lead to some locking timeout in the execution.

+
    +
  • Throughput capacity of your ElasticSearch
  • +
  • Number of workers started at the same time
  • +
  • Dependencies between data
  • +
  • Merging capacity of OpenCTI
  • +
+

If you have this kind of error, limit the number of workers deployed. Try to find the right balance of the number of workers, connectors and elasticsearch sizing.

+
+

Ingestion functional errors

+
+

Indicator of type yara is not correctly formatted

+

OpenCTI check the validity of the indicator rule.

+
+
+

Observable of type IPv4-Addr is not correctly formatted

+

OpenCTI check the validity of the observable value.

+
+

Dependencies errors

+
+

TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark...

+

Disk full, no space left on the device for ElasticSearch.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/deployment/upgrade/index.html b/6.2.X/deployment/upgrade/index.html new file mode 100755 index 00000000..73b5dc31 --- /dev/null +++ b/6.2.X/deployment/upgrade/index.html @@ -0,0 +1,5041 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Upgrade - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Upgrade

+

Depending on your installation mode, upgrade path may change.

+
+

Migrations

+

The platform is taking care of all necessary underlying migrations in the databases if any. You can upgrade OpenCTI from any version to the latest one, including skipping multiple major releases.

+
+

Using Docker

+

Before applying this procedure, please update your docker-compose.yml file with the new version number of container images.

+

For single node Docker

+
$ sudo docker-compose stop
+$ sudo docker-compose pull
+$ sudo docker-compose up -d
+
+

For Docker swarm

+

For each of services, you have to run the following command:

+
$ sudo docker service update --force service_name
+
+

Manual installation

+

When upgrading the platform, you have to replace all files and restart the platform, the database migrations will be done automatically:

+
$ yarn serv
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/api-usage/index.html b/6.2.X/development/api-usage/index.html new file mode 100755 index 00000000..2cb324e7 --- /dev/null +++ b/6.2.X/development/api-usage/index.html @@ -0,0 +1,5124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Playground - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

GraphQL playground

+

The GraphQL playground is an integrated development environment (IDE) provided by OpenCTI for exploring and testing GraphQL APIs. It offers a user-friendly interface that allows developers to interactively query the GraphQL schema, experiment with different queries, and visualize the responses.

+

Key features

+

Interactive querying

+

The Playground provides a text editor where developers can write GraphQL queries, mutations, and subscriptions. As you type, the Playground offers syntax highlighting, autocompletion, and error checking to aid in query composition.

+

Playground interactive query

+

Documentation

+

Developers can access comprehensive documentation for the GraphQL schema directly within the Playground. This documentation includes descriptions of all available types, fields, and directives, making it easy to understand the data model and construct queries.

+

Playground documentation

+

Query history

+

The playground keeps track of previously executed queries, allowing developers to revisit and reuse queries from previous sessions. This feature streamlines the development process by eliminating the need to retype complex queries.

+

Playground query history

+

Response visualization

+

Upon executing a query, the playground displays the response data in a structured and readable format. JSON responses are presented in a collapsible tree view, making it easy to navigate nested data structures and inspect individual fields.

+

Playground response visualization

+

Schema exploration

+

Developers can explore the GraphQL schema using the built-in schema viewer. This feature provides a graphical representation of the schema, showing types, fields, and their relationships. Developers can explore the schema and understand its structure.

+

Playground schema

+

Getting started

+

To access the GraphQL playground, navigate to the GraphQL endpoint of your OpenCTI instance: https://[your-opencti-instance]/graphql. Then, follow these steps to utilize the playground:

+
    +
  1. Query editor: Write GraphQL queries, mutations, and subscriptions in the text editor. Use syntax highlighting and autocompletion to speed up query composition.
  2. +
  3. Documentation explorer: Access documentation for the GraphQL schema by clicking on the "Docs" tab on the right. Browse types, fields, and descriptions to understand the available data and query syntax.
  4. +
  5. Query history: View and execute previously executed queries from the "History" tab on the top. Reuse queries and experiment with variations without retyping.
  6. +
  7. Response pane: Visualize query responses in the response pane. Expand and collapse sections to navigate complex data structures and inspect individual fields.
  8. +
  9. Schema viewer: Explore the GraphQL schema interactively using the "Schema" tab on the right. Navigate types, fields, and relationships to understand the data model and plan queries.
  10. +
+

External Resources

+

For a more in-depth understanding of GraphQL and its usage, consider exploring the following external resources:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/assets/playground-documentation.png b/6.2.X/development/assets/playground-documentation.png new file mode 100755 index 00000000..8b3fe0f1 Binary files /dev/null and b/6.2.X/development/assets/playground-documentation.png differ diff --git a/6.2.X/development/assets/playground-interactive-query.png b/6.2.X/development/assets/playground-interactive-query.png new file mode 100755 index 00000000..2138f825 Binary files /dev/null and b/6.2.X/development/assets/playground-interactive-query.png differ diff --git a/6.2.X/development/assets/playground-query-history.png b/6.2.X/development/assets/playground-query-history.png new file mode 100755 index 00000000..d84606f2 Binary files /dev/null and b/6.2.X/development/assets/playground-query-history.png differ diff --git a/6.2.X/development/assets/playground-response-visualization.png b/6.2.X/development/assets/playground-response-visualization.png new file mode 100755 index 00000000..789c93c0 Binary files /dev/null and b/6.2.X/development/assets/playground-response-visualization.png differ diff --git a/6.2.X/development/assets/playground-schema.png b/6.2.X/development/assets/playground-schema.png new file mode 100755 index 00000000..adbb9376 Binary files /dev/null and b/6.2.X/development/assets/playground-schema.png differ diff --git a/6.2.X/development/connectors/index.html b/6.2.X/development/connectors/index.html new file mode 100755 index 00000000..350c1cf6 --- /dev/null +++ b/6.2.X/development/connectors/index.html @@ -0,0 +1,5876 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Connectors - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Connector development

+

Introduction

+

A connector in OpenCTI is a service that runs next to the platform and can be implemented in almost any programming language that has STIX2 support. Connectors are used to extend the functionality of OpenCTI and allow operators to shift some of the processing workload to external services. To use the conveniently provided OpenCTI connector SDK you need to use Python3 at the moment.

+

We choose to have a very decentralized approach on connectors, in order to bring a maximum freedom to developers and vendors. So a connector on OpenCTI can be defined by a standalone Python 3 process that pushes an understandable format of data to an ingestion queue of messages.

+

Each connector must implement a long-running process that can be launched just by executing the main Python file. The only mandatory dependency is the OpenCTIConnectorHelper class that enables the connector to send data to OpenCTI.

+

Getting started

+

In the beginning first think about your use-case to choose an appropriate connector type - what do want to achieve with your connector? The following table gives you an overview of the current connector types and some typical use-cases:

+

Connector types

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeTypical use casesExample connector
EXTERNAL_IMPORTIntegrate external TI provider, Integrate external TI platformAlienVault
INTERNAL_ENRICHMENTEnhance existing data with additional knowledgeAbuseIP
INTERNAL_IMPORT_FILE(Bulk) import knowledge from filesImport document
INTERNAL_EXPORT_FILE(Bulk) export knowledge to filesSTIX 2.1, CSV.
STREAMIntegrate external TI provider, Integrate external TI platformElastic Security
+

After you've selected your connector type make yourself familiar with STIX2 and the supported relationships in OpenCTI. Having some knowledge about the internal data models with help you a lot with the implementation of your idea.

+

Preparation

+

Environment Setup

+

To develop and test your connector, you need a running OpenCTI instance with the frontend and the messaging broker accessible. If you don't plan on developing anything for the OpenCTI platform or the frontend, the easiest setup for the connector development is using the docker setup, For more details see here.

+

Coding Setup

+

To give you an easy starting point we prepared an example connector in the public repository you can use as template to bootstrap your development.

+

Some prerequisites we recommend to follow this tutorial:

+ +

In the terminal check out the connectors repository and copy the template connector to $myconnector (replace it with your name throughout the following text examples).

+
$ pip3 install black flake8 pycti
+# Fork the current repository, then clone your fork
+$ git clone https://github.com/YOUR-USERNAME/connectors.git
+$ cd connectors
+$ git remote add upstream https://github.com/OpenCTI-Platform/connectors.git
+# Create a branch for your feature/fix
+$ git checkout -b [branch-name]
+# Copy the appropriate template directory for the connector type
+$ cp -r templates/$connector_type $connector_type/$myconnector
+$ cd $connector_type/$myconnector
+$ ls -R
+Dockerfile              docker-compose.yml      requirements.txt
+README.md               entrypoint.sh           src
+
+./src:
+lib     main.py
+
+./src/lib:
+$connector_type.py
+
+

Changing the template

+

There are a few files in the template we need to change for our connector to be unique. You can check for all places you need to change you connector name with the following command (the output will look similar):

+
$ grep -Ri template .
+
+README.md:# OpenCTI Template Connector
+README.md:| `connector_type`                     | `CONNECTOR_TYPE`                    | Yes          | Must be `Template_Type` (this is the connector type).                                                                                                      |
+README.md:| `connector_name`                     | `CONNECTOR_NAME`                    | Yes          | Option `Template`                                                                                                                                          |
+README.md:| `connector_scope`                    | `CONNECTOR_SCOPE`                   | Yes          | Supported scope: Template Scope (MIME Type or Stix Object)                                                                                                 |
+README.md:| `template_attribute`                 | `TEMPLATE_ATTRIBUTE`                | Yes          | Additional setting for the connector itself                                                                                                                |
+docker-compose.yml:  connector-template:
+docker-compose.yml:    image: opencti/connector-template:4.5.5
+docker-compose.yml:      - CONNECTOR_TYPE=Template_Type
+docker-compose.yml:      - CONNECTOR_NAME=Template
+docker-compose.yml:      - CONNECTOR_SCOPE=Template_Scope # MIME type or Stix Object
+entrypoint.sh:cd /opt/opencti-connector-template
+Dockerfile:COPY src /opt/opencti-template
+Dockerfile:    cd /opt/opencti-connector-template && \
+src/main.py:class Template:
+src/main.py:            "TEMPLATE_ATTRIBUTE", ["template", "attribute"], config, True
+src/main.py:        connectorTemplate = Template()
+src/main.py:        connectorTemplate.run()
+src/config.yml.sample:  type: 'Template_Type'
+src/config.yml.sample:  name: 'Template'
+src/config.yml.sample:  scope: 'Template_Scope' # MIME type or SCO
+
+

Required changes:

+
    +
  • Change Template or templatementions to your connector name e.g. ImportCsv or importcsv
  • +
  • Change TEMPLATE mentions to your connector name e.g. IMPORTCSV
  • +
  • Change Template_Scope mentions to the required scope of your connector. For processing imported files, that can be the Mime type e.g. application/pdf or for enriching existing information in OpenCTI, define the STIX object's name e.g. Report. Multiple scopes can be separated by a simple ,
  • +
  • Change Template_Type to the connector type you wish to develop. The OpenCTI types are defined hereafter:
      +
    • EXTERNAL_IMPORT
    • +
    • INTERNAL_ENRICHMENT
    • +
    • INTERNAL_EXPORT_FILE
    • +
    • INTERNAL_IMPORT_FILE
    • +
    • STREAM
    • +
    +
  • +
+

Development

+

Initialize the OpenCTI connector helper

+

After getting the configuration parameters of your connector, you have to initialize the OpenCTI connector helper by using the pycti Python library. This is shown in the following example:

+
class TemplateConnector:
+    def __init__(self):
+                # Instantiate the connector helper from config
+        config_file_path = os.path.dirname(os.path.abspath(__file__)) + "/config.yml"
+        config = (
+            yaml.load(open(config_file_path), Loader=yaml.SafeLoader)
+            if os.path.isfile(config_file_path)
+            else {}
+        )
+        self.helper = OpenCTIConnectorHelper(config)
+                self.custom_attribute = get_config_variable(
+            "TEMPLATE_ATTRIBUTE", ["template", "attribute"], config
+        )
+
+

Since there are some basic differences in the tasks of the different connector classes, the structure is also a bit class dependent. While the external-import and the stream connector run independently in a regular interval or constantly, the other 3 connector classes only run when being requested by the OpenCTI platform.

+

The self-triggered connectors run independently, but the OpenCTI need to define a callback function, which can be executed for the connector to start its work. This is done via self.helper.listen(self._process_message). In the appended examples, the difference of the setup can be seen.

+

Self-triggered Connectors

+
    +
  • external-import
  • +
  • stream
  • +
+

OpenCTI triggered

+
    +
  • internal-enrichment
  • +
  • internal-import
  • +
  • internal-export
  • +
+
from pycti import OpenCTIConnectorHelper, get_config_variable
+
+class TemplateConnector:
+    def __init__(self) -> None:
+                # Initialization procedures
+                [...]
+        self.template_interval = get_config_variable(
+            "TEMPLATE_INTERVAL", ["template", "interval"], config, True
+        )
+
+    def get_interval(self) -> int:
+        return int(self.template_interval) * 60 * 60 * 24
+
+    def run(self) -> None:
+                # Main procedure
+
+if __name__ == "__main__":
+    try:
+        template_connector = TemplateConnector()
+        template_connector.run()
+    except Exception as e:
+        print(e)
+        time.sleep(10)
+        exit(0)
+
+
from pycti import OpenCTIConnectorHelper, get_config_variable
+
+class TemplateConnector:
+    def __init__(self) -> None:
+                # Initialization procedures
+                [...]
+
+    def _process_message(self, data: dict) -> str:
+                # Main procedure                
+
+    # Start the main loop
+    def start(self) -> None:
+        self.helper.listen(self._process_message)
+
+if __name__ == "__main__":
+    try:
+        template_connector = TemplateConnector()
+        template_connector.start()
+    except Exception as e:
+        print(e)
+        time.sleep(10)
+        exit(0)
+
+

Write and Read Operations

+

When using the OpenCTIConnectorHelper class, there are two way for reading from or writing data to the OpenCTI platform.

+
    +
  1. via the OpenCTI API interface via self.helper.api
  2. +
  3. via the OpenCTI worker via self.send_stix2_bundle
  4. +
+

Sending data to the OpenCTI platform

+

The recommended way for creating or updating data in the OpenCTI platform is via the OpenCTI worker. This enables the connector to just send and forget about thousands of entities at once to without having to think about the ingestion order, performance or error handling.

+ + +

The OpenCTI connector helper method send_stix2_bundle must be used to send data to OpenCTI. The send_stix2_bundle function takes 2 arguments.

+
    +
  1. A serialized STIX2 bundle as a string (mandatory)
  2. +
  3. A list of entities types that should be ingested (optional)
  4. +
+

Here is an example using the STIX2 Python library:

+
from stix2 import Bundle, AttackPattern
+
+[...]
+
+attack_pattern = AttackPattern(name='Evil Pattern')
+
+bundle_objects = []
+bundle_objects.append(attack_pattern)
+
+bundle = Bundle(objects=bundle_objects).serialize()
+bundles_sent = self.opencti_connector_helper.send_stix2_bundle(bundle)
+
+

Reading from the OpenCTI platform

+

Read queries to the OpenCTI platform can be achieved using the API and the STIX IDs can be attached to reports to create the relationship between those two entities.

+
entity = self.helper.api.vulnerability.read(
+    filters={"key": "name", "values": ["T1234"]}
+)
+
+

If you want to add the found entity via objects_refs to another SDO, simply add a list of stix_ids to the SDO. Here's an example using the entity from the code snippet above:

+
from stix2 import Report
+
+[...]
+
+report = Report(
+    id=report["standard_id"],
+  object_refs=[entity["standard_id"]],
+)
+
+

Logging

+

When something crashes for a user, you as a developer want to know as much as possible about this incident to easily improve your code and remove this issue. To do so, it is very helpful if your connector documents what it does. Use info messages for big changes like the beginning or the finishing of an operation, but to facilitate your bug removal attempts, implement debug messages for minor operation changes to document different steps in your code.

+

When encountering a crash, the connector's user can easily restart the troubling connector with the debug logging activated.

+
    +
  • CONNECTOR_LOG_LEVEL=debug
  • +
+

Using those additional log messages, the bug report is more enriched with information about the possible cause of the problem. Here's an example of how the logging should be implemented:

+
        def run(self) -> None:
+                self.helper.log_info('Template connector starts')
+                results = self._ask_for_news()
+                [...]
+
+        def _ask_for_news() -> None:
+                overall = []
+                for i in range(0, 10):
+                        self.log_debug(f"Asking about news with count '{i}'")
+                        # Do something
+                        self.log_debug(f"Resut: '{result}'")
+                        overall.append(result)
+                return overall
+
+

Please make sure that the debug messages rich of useful information, but that they are not redundant and that the user is not drowned by unnecessary information.

+

Additional implementations

+

If you are still unsure about how to implement certain things in your connector, we advise you to have a look at the code of other connectors of the same type. Maybe they are already using an approach which is suitable for addressing to your problem.

+

OpenCTI triggered Connector - Special cases

+

Data Layout of Dictionary from Callback function

+

OpenCTI sends the connector a few instructions via the data dictionary in the callback function. Depending on the connector type, the data dictionary content is a bit different. Here are a few examples for each connector type.

+

Internal Import Connector

+
{ 
+  "file_id": "<fileId>",
+  "file_mime": "application/pdf", 
+  "file_fetch": "storage/get/<file_id>", // Path to get the file
+  "entity_id": "report--82843863-6301-59da-b783-fe98249b464e", // Context of the upload
+}
+
+

Internal Enrichment Connector

+
{ 
+  "entity_id": "<stixCoreObjectId>" // StixID of the object wanting to be enriched
+}
+
+

Internal Export Connector

+
{ 
+  "export_scope": "single", // 'single' or 'list'
+  "export_type": "simple", // 'simple' or 'full'
+  "file_name": "<fileName>", // Export expected file name
+  "max_marking": "<maxMarkingId>", // Max marking id
+  "entity_type": "AttackPattern", // Exported entity type
+  // ONLY for single entity export
+  "entity_id": "<entity.id>", // Exported element
+  // ONLY for list entity export
+  "list_params": "[<parameters>]" // Parameters for finding entities
+}
+
+

Self triggered Connector - Special cases

+

Initiating a 'Work' before pushing data

+

For self-triggered connectors, OpenCTI has to be told about new jobs to process and to import. This is done by registering a so called work before sending the stix bundle and signalling the end of a work. Here an example:

+

By implementing the work registration, they will show up as shown in this screenshot for the MITRE ATT&CK connector:

+
def run() -> None:
+        # Anounce upcoming work
+        timestamp = int(time.time())
+        now = datetime.utcfromtimestamp(timestamp)
+    friendly_name = "Template run @ " + now.strftime("%Y-%m-%d %H:%M:%S")
+    work_id = self.helper.api.work.initiate_work(
+                self.helper.connect_id, friendly_name
+        )
+
+        [...]
+        # Send Stix bundle
+        self.helper.send_stix2_bundle(
+                bundle,
+                entities_types=self.helper.connect_scope,
+                update=True,
+                work_id=work_id,
+        )
+        # Finish the work
+        self.helper.log_info(
+            f"Connector successfully run, storing last_run as {str(timestamp)}"
+    )              
+        message = "Last_run stored, next run in: {str(round(self.get_interval() / 60 / 60 / 24, 2))} days"
+        self.helper.api.work.to_processed(work_id, message)
+
+

Interval handling

+

The connector is also responsible for making sure that it runs in certain intervals. In most cases, the intervals are definable in the connector config and then only need to be set and updated during the runtime.

+
class TemplateConnector:
+    def __init__(self) -> None:
+                # Initialization procedures
+                [...]
+        self.template_interval = get_config_variable(
+            "TEMPLATE_INTERVAL", ["template", "interval"], config, True
+        )
+
+    def get_interval(self) -> int:
+        return int(self.template_interval) * 60 * 60 * 24
+
+        def run(self) -> None:
+        self.helper.log_info("Fetching knowledge...")
+        while True:
+            try:
+                # Get the current timestamp and check
+                timestamp = int(time.time())
+                current_state = self.helper.get_state()
+                if current_state is not None and "last_run" in current_state:
+                    last_run = current_state["last_run"]
+                    self.helper.log_info(
+                        "Connector last run: "
+                        + datetime.utcfromtimestamp(last_run).strftime(
+                            "%Y-%m-%d %H:%M:%S"
+                        )
+                    )
+                else:
+                    last_run = None
+                    self.helper.log_info("Connector has never run")
+                # If the last_run is more than interval-1 day
+                if last_run is None or (
+                    (timestamp - last_run)
+                    > ((int(self.template_interval) - 1) * 60 * 60 * 24)
+                ):
+                    timestamp = int(time.time())
+                    now = datetime.utcfromtimestamp(timestamp)
+                    friendly_name = "Connector run @ " + now.strftime("%Y-%m-%d %H:%M:%S")
+
+                                        ###
+                                        # RUN CODE HERE     
+                                        ###
+
+                    # Store the current timestamp as a last run
+                    self.helper.log_info(
+                        "Connector successfully run, storing last_run as "
+                        + str(timestamp)
+                    )
+                    self.helper.set_state({"last_run": timestamp})
+                    message = (
+                        "Last_run stored, next run in: "
+                        + str(round(self.get_interval() / 60 / 60 / 24, 2))
+                        + " days"
+                    )
+                    self.helper.api.work.to_processed(work_id, message)
+                    self.helper.log_info(message)
+                    time.sleep(60)
+                else:
+                    new_interval = self.get_interval() - (timestamp - last_run)
+                    self.helper.log_info(
+                        "Connector will not run, next run in: "
+                        + str(round(new_interval / 60 / 60 / 24, 2))
+                        + " days"
+                    )
+                    time.sleep(60)
+
+

Running the connector

+

For development purposes, it is easier to simply run the python script locally until everything works as it should.

+
$ virtualenv env
+$ source ./env/bin/activate
+$ pip3 install -r requirements
+$ cp config.yml.sample config.yml
+# Define the opencti url and token, as well as the connector's id
+$ vim config.yml
+$ python3 main.py
+INFO:root:Listing Threat-Actors with filters null.
+INFO:root:Connector registered with ID: a2de809c-fbb9-491d-90c0-96c7d1766000
+INFO:root:Starting ping alive thread
+...
+
+

Final Testing

+

Before submitting a Pull Request, please test your code for different use cases and scenarios. We don't have an automatic testing suite for the connectors yet, thus we highly depend on developers thinking about creative scenarios their code could encounter.

+

Prepare for release

+

If you plan to provide your connector to be used by the community (❤️) your code should pass the following (minimum) criteria.

+
# Linting with flake8 contains no errors or warnings
+$ flake8 --ignore=E,W
+# Verify formatting with black
+$ black .
+All done!  🍰 ✨
+1 file left unchanged.
+# Verify import sorting
+$ isort --profile black .
+Fixing /path/to/connector/file.py
+# Push you feature/fix on Github
+$ git add [file(s)]
+$ git commit -m "[connector_name] descriptive message"
+$ git push origin [branch-name]
+# Open a pull request with the title "[connector_name] message"
+
+

If you have any trouble with this just reach out to the OpenCTI core team. We are happy to assist with this.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/environment_ubuntu/index.html b/6.2.X/development/environment_ubuntu/index.html new file mode 100755 index 00000000..5acf8260 --- /dev/null +++ b/6.2.X/development/environment_ubuntu/index.html @@ -0,0 +1,5044 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ubuntu - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Prerequisites Ubuntu

+

Development stack require some base software that need to be installed.

+

Docker or podman

+

Platform dependencies in development are deployed through container management, so you need to install a container stack.

+

We currently support docker and podman.

+
$ sudo apt-get install docker docker-compose curl
+
+
sudo apt-get -y install podman
+
+

As OpenCTI has a dependency to ElasticSearch, you have to set the vm.max_map_count before running the containers, as mentioned in the ElasticSearch documentation.

+
$ sudo sysctl -w vm.max_map_count=262144
+
+

NodeJS and yarn

+

The platform is developed on nodejs technology, so you need to install node and the yarn package manager.

+
$ sudo apt-get install nodejs
+$ sudo curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+$ sudo echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+$ sudo apt-get update && sudo apt-get install yarn
+
+

Python runtime

+

For worker and connectors, a python runtime is needed.

+
$ sudo apt-get install python3 python3-pip
+
+

Git and dev tool

+
    +
  • Install Git from apt
  • +
+
$ sudo apt-get install git-all
+
+
    +
  • Install your preferred IDE
      +
    • Intellij community edition - https://www.jetbrains.com/idea/download/
    • +
    • VSCode - https://code.visualstudio.com/
    • +
    +
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/environment_windows/index.html b/6.2.X/development/environment_windows/index.html new file mode 100755 index 00000000..7156edf6 --- /dev/null +++ b/6.2.X/development/environment_windows/index.html @@ -0,0 +1,5069 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Windows - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Prerequisites Windows

+

Development stack require some base software that need to be installed.

+

Docker or podman

+

Platform dependencies in development are deployed through container management, so you need to install a container stack.

+

We currently support docker and postman.

+

Docker Desktop from - https://docs.docker.com/desktop/install/windows-install/

+ +

wsl --set-default-version 2

+
    +
  • Reboot computer and continue to next step
  • +
  • Load Docker Application
  • +
  • NOTE DOCKER LICENSE - You are agreeing to the licence for Non-commercial Open Source Project use. OpenCTI is Open Source and the version you would be possibly contributing to enhancing is the unpaid non-commercial/non-enterprise version. If you intention is different - please consult with your organization's legal/licensing department.
  • +
  • Leave Docker Desktop running
  • +
+

NodeJS and yarn

+

The platform is developed on nodejs technology, so you need to install node and the yarn package manager.

+ +

Python runtime

+

For worker and connectors, a python runtime is needed. Even if you already have a python runtime installed through node installation, +on windows some nodejs package will be recompiled with python and C++ runtime.

+

For this reason Visual Studio Build Tools is required.

+ +

Git and dev tool

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/platform/index.html b/6.2.X/development/platform/index.html new file mode 100755 index 00000000..862d7677 --- /dev/null +++ b/6.2.X/development/platform/index.html @@ -0,0 +1,5770 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Platform - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Platform development

+

Introduction

+

This summary should give you a detailed setup description for initiating the OpenCTI setup environment +necessary for developing on the OpenCTI platform, a client library or the connectors. +This page document how to set up an "All-in-One" development environment for OpenCTI. +The devenv will contain data of 3 different repositories:

+ +

Platform

+

Contains the platform OpenCTI project code base:

+
    +
  • docker-compose (docker or podman) ~/opencti/opencti-platform/opencti-dev
  • +
  • Web frontend (nodejs / react) ~/opencti/opencti-platform/opencti-graphql
  • +
  • Backend (nodejs) ~/opencti/opencti-platform/opencti-frontend
  • +
  • Worker (nodejs / python) ~/opencti/opencti-worker
  • +
+

Connectors

+

Contains a lot of developed connectors, as a source of inspiration for your new connector.

+

Client python

+

Contains the source code of the python library used in worker or connectors.

+

Prerequisites

+

Some tools are needed before starting to develop. Please check Ubuntu prerequisites or Windows prerequisites

+

Clone the projects

+

Fork and clone the git repositories

+ +

Dependencies containers

+

In development dependencies are deployed trough containers. +A development compose file is available in ~/opencti/opencti-platform/opencti-dev

+
cd ~/docker
+#Start the stack in background
+docker-compose -f ./docker-compose-dev.yml up -d
+
+

You now have all the dependencies of OpenCTI running and waiting for product to run.

+

Backend / API

+

Python virtual env

+

The GraphQL API is developed in JS and with some python code. +As it's an "all-in-one" installation, the python environment will be installed in a virtual environment.

+
cd ~/opencti/opencti-platform/opencti-graphql
+python3 -m venv .venv --prompt "graphql"
+source .venv/bin/activate
+pip install --upgrade pip wheel setuptools
+yarn install
+yarn install:python 
+deactivate
+
+

Development configuration

+

The API can be specifically configured with files depending on the starting profile. +By default, the default.json file is used and will be correctly configured for local usage except for admin password.

+

So you need to create a development profile file. You can duplicate the default file and adapt if you need. +

cd ~/opencti/opencti-platform/opencti-graphql/config
+cp default.json development.json
+

+

At minimum adapt the admin part for the password and token. +

    "admin": {
+      "email": "admin@opencti.io",
+      "password": "MyNewPassord",
+      "token": "UUID generated with https://www.uuidgenerator.net"
+    }
+

+

Install / start

+

Before starting the backend you need to install the nodejs modules

+
cd ~/opencti/opencti-platform/opencti-graphql
+yarn install
+
+

Then you can simply start the backend API with the yarn start command

+
cd ~/opencti/opencti-platform/opencti-graphql
+yarn start
+
+

The platform will start logging some interesting information

+
{"category":"APP","level":"info","message":"[OPENCTI] Starting platform","timestamp":"2023-07-02T16:37:10.984Z","version":"5.8.7"}
+{"category":"APP","level":"info","message":"[OPENCTI] Checking dependencies statuses","timestamp":"2023-07-02T16:37:10.987Z","version":"5.8.7"}
+{"category":"APP","level":"info","message":"[SEARCH] Elasticsearch (8.5.2) client selected / runtime sorting enabled","timestamp":"2023-07-02T16:37:11.014Z","version":"5.8.7"}
+{"category":"APP","level":"info","message":"[CHECK] Search engine is alive","timestamp":"2023-07-02T16:37:11.015Z","version":"5.8.7"}
+...
+{"category":"APP","level":"info","message":"[INIT] Platform initialization done","timestamp":"2023-07-02T16:37:11.622Z","version":"5.8.7"}
+{"category":"APP","level":"info","message":"[OPENCTI] API ready on port 4000","timestamp":"2023-07-02T16:37:12.382Z","version":"5.8.7"}
+
+

If you want to start on another profile you can use the -e parameter. +For example here to use the profile.json configuration file.

+
yarn start -e profile
+
+

Code check

+

Before pushing your code you need to validate the syntax and ensure the testing will be validated.

+

For validation

+

yarn lint

+

yarn check-ts

+

For testing

+

For starting the test you will need to create a test.json configuration file. +You can use the same dependencies by only adapting all prefix for all dependencies.

+

Tests are using dedicated indices in the Elastic database (prefixed with test-* or the prefix that you have set up in test.json).

+

The following command will run the complete test suite using vitest, which might take more than 30 minutes. +It starts by cleaning up the test database and seeding a minimal dataset. +The file vitest.config.test.ts can be edited to run only a specific file pattern.

+

yarn test:dev

+

We also provide utility scripts to ease the development of new tests, especially integration tests that rely on the sample data +loaded after executing 00-inject/loader-test.ts.

+

To solely initialize the test database with this sample dataset run:

+

yarn test:dev:init

+

And then, execute the following command to run the pattern specified in the file vitest.config.test.ts, or add a file name +to the command line to run only this test file.

+

yarn test:dev:resume

+

This last command will NOT cleanup & initialize the test database and thus will be quicker to execute.

+

Frontend

+

Install / start

+

Before starting the backend you need to install the nodejs modules

+
cd ~/opencti/opencti-platform/opencti-front
+yarn install
+
+

Then you can simply start the frontend with the yarn start command

+
cd ~/opencti/opencti-platform/opencti-front
+yarn start
+
+

The frontend will start with some interesting information

+
[INFO] [default] compiling...
+[INFO] [default] compiled documents: 1592 reader, 1072 normalization, 1596 operation text
+[INFO] Compilation completed.
+[INFO] Done.
+[HPM] Proxy created: /stream  -> http://localhost:4000
+[HPM] Proxy created: /storage  -> http://localhost:4000
+[HPM] Proxy created: /taxii2  -> http://localhost:4000
+[HPM] Proxy created: /feeds  -> http://localhost:4000
+[HPM] Proxy created: /graphql  -> http://localhost:4000
+[HPM] Proxy created: /auth/**  -> http://localhost:4000
+[HPM] Proxy created: /static/flags/**  -> http://localhost:4000
+
+

The web UI should be accessible on http://127.0.0.1:3000

+

Code check

+

Before pushing your code you need to validate the syntax and ensure the testing will be validated.

+

For validation

+

yarn lint

+

yarn check-ts

+

For testing

+

yarn test

+

Worker

+

Running a worker is required when you want to develop on the ingestion or import/export connectors.

+

Python virtual env

+
cd ~/opencti/opencti-worker/src
+python3 -m venv .venv --prompt "worker"
+source .venv/bin/activate
+pip3 install --upgrade pip wheel setuptools
+pip3 install -r requirements.txt
+deactivate
+
+

Install / start

+
cd ~/opencti/opencti-worker/src
+source .venv/bin/activate
+python worker.py
+
+

Connectors

+

For connectors development, please take a look to Connectors development dedicated page.

+

Production build

+

Based on development source you can build the package for production. +This package will be minified and optimized with esbuild.

+
$ cd opencti-frontend
+$ yarn build
+$ cd ../opencti-graphql
+$ yarn build
+
+

After the build you can start the production build with yarn serv. +This build will use the production.json configuration file.

+
$ cd ../opencti-graphql
+$ yarn serv
+
+

Continuous Integration and features cross repository

+

When a feature requires changes in two or more repositories in opencti, connectors and client-python; then some specific convention must be used to have the continuous integration build them all together.

+

Naming convention of branch

+

The Pull Request on opencti repository should be (issue or bug)/number + optional, example: issue/7062-contributing

+

The pull request on connector or client-python should refer to the opencti one by starting with "opencti/" and then the same name. Example: opencti/issue/7062-contributing

+

Note that if there are several matches, the first one is taken. So for example having issue/7062-contributing and issue/7062 that are both marked as "multi-repository" is not a good idea.

+

Labels

+

All Pull Requests involved must have the label multi-repository added in GitHub.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/development/python/index.html b/6.2.X/development/python/index.html new file mode 100755 index 00000000..c699ce1e --- /dev/null +++ b/6.2.X/development/python/index.html @@ -0,0 +1,4900 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Python library - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Python library

+

The PyCTI library is the official Python client for OpenCTI. It is made to help developers interact with the openCTI plaform.

+

Installation

+

To install the latest Python client library, please use pip:

+
$ pip3 install pycti
+
+

Using the helper functions

+

The main class OpenCTIApiClient contains all what you need to interact with the platform, you just have to initialize it.

+

The following example shows how you create an indicator in OpenCTI using the python library with TLP marking and OpenCTI compatible date format.

+
from dateutil.parser import parse
+from pycti import OpenCTIApiClient
+from stix2 import TLP_GREEN
+
+# OpenCTI API client initialization
+opencti_api_client = OpenCTIApiClient("https://myopencti.server", "mysupersecrettoken")
+
+# Define an OpenCTI compatible date
+date = parse("2019-12-01").strftime("%Y-%m-%dT%H:%M:%SZ")
+
+# Get the OpenCTI marking for stix2 TLP_GREEN
+TLP_GREEN_CTI = opencti_api_client.marking_definition.read(id=TLP_GREEN["id"])
+
+# Use the client to create an indicator in OpenCTI
+indicator = opencti_api_client.indicator.create(
+    name="C2 server of the new campaign",
+    description="This is the C2 server of the campaign",
+    pattern_type="stix",
+    pattern="[domain-name:value = 'www.5z8.info']",
+    x_opencti_main_observable_type="IPv4-Addr",
+    valid_from=date,
+    update=True,
+    markingDefinitions=[TLP_GREEN_CTI["id"]],
+)
+
+

Examples

+

A suite of illustrative examples is available in the PyCTI GitHub repository to aid in better understanding.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/index.html b/6.2.X/index.html new file mode 100755 index 00000000..c671408f --- /dev/null +++ b/6.2.X/index.html @@ -0,0 +1,5014 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +

OpenCTI Documentation Space

+

Welcome to the OpenCTI Documentation space. Here you will be able to find all documents, meeting notes and presentations about the platform.

+
+

Release notes

+

Please, be sure to also take a look at the OpenCTI releases notes, they may contain important information about releases and deployments.

+
+

Introduction

+

OpenCTI is an open source platform allowing organizations to manage their cyber threat intelligence knowledge and observables. It has been created in order to structure, store, organize and visualize technical and non-technical information about cyber threats.

+

Getting started

+
+
    +
  • +

    Deployment & Setup

    +
    +

    Learn how to deploy and configure the platform as well as +launch connectors to get the first data in OpenCTI.

    +

    Deploy now

    +
  • +
  • +

    User Guide

    +
    +

    Understand how to use the platform, explore the knowledge, import +and export information, create dashboard, etc.

    +

    Explore

    +
  • +
  • +

    Administration

    +
    +

    Know how to administrate OpenCTI, create users and groups using RBAC / +segregation, put retention policies and custom taxonomies.

    +

    Customize

    +
  • +
+
+
+

Need more help?

+

We are doing our best to keep this documentation complete, accurate and up to date.

+

If you still have questions or you find something which is not sufficiently explained, join the Filigran Community on Slack.

+
+

Latest blog posts

+

All tutorials are published directly on the Medium blog, this section provides a comprehensive list of the most important ones.

+
+
    +
  • +

    Introducing decay rules implementation for Indicators in OpenCTI
    + Mar 25, 2024

    +

    Cyber Threat Intelligence is made to be used. To be useful, it must be relevant and on time. It is why managing the lifecycle of Indicators of Compromise...

    +

    Read

    +
  • +
  • +

    Introducing advanced filtering possibilities in OpenCTI
    + Feb 5, 2024

    +

    CTI databases are usually vast and made of complex, inter-dependent objects ingested from various sources. In this challenging context, cyber analysts need...

    +

    Read

    +
  • +
  • +

    Breaking change: evolution of the way Connector, Streams and Feeds import data in OpenCTI
    + Jan 29, 2024

    +

    How Connectors, Feeds and Streams use Confidence level currently...

    +

    Read

    +
  • +
+
+

Additional resources

+

Below, you will find external resources which may be useful along your OpenCTI journey.

+
+

OpenCTI Ecosystem
+List of available connectors and integrations to expand platform usage.

+

Training Courses
+Training courses for analysts and administrators in the Filigran training center.

+

Video materials
+Set of video illustrating the implementation of use cases and platform capabilities.

+
+




+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/js/timeago.min.js b/6.2.X/js/timeago.min.js new file mode 100755 index 00000000..a8530a5f --- /dev/null +++ b/6.2.X/js/timeago.min.js @@ -0,0 +1,2 @@ +/* Taken from https://cdnjs.cloudflare.com/ajax/libs/timeago.js/4.0.2/timeago.min.js */ +!function(s,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((s=s||self).timeago={})}(this,function(s){"use strict";var a=["second","minute","hour","day","week","month","year"];function n(s,n){if(0===n)return["just now","right now"];var e=a[Math.floor(n/2)];return 1=m[t]&&t=m[e]&&e 0) { + var locale = nodes[0].getAttribute('locale'); + timeago.render(nodes, locale); + } + }) +} else { + var nodes = document.querySelectorAll('.timeago'); + if (nodes.length > 0) { + var locale = nodes[0].getAttribute('locale'); + timeago.render(nodes, locale); + } +} diff --git a/6.2.X/reference/api/index.html b/6.2.X/reference/api/index.html new file mode 100755 index 00000000..542a8d84 --- /dev/null +++ b/6.2.X/reference/api/index.html @@ -0,0 +1,5214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GraphQL API - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

GraphQL API

+

OpenCTI provides a comprehensive API based on GraphQL, allowing users to perform various actions programmatically. The API enables users to interact with OpenCTI's functionality and data, offering a powerful tool for automation, integration, and customization. All actions that can be performed through the platform's graphical interface are also achievable via the API.

+

Authentication

+

Access to the OpenCTI API requires authentication using standard authentication mechanisms. Access rights to data via the API will be determined by the access privileges of the user associated with the API key. For authentication, users need to include the following headers in their API requests:

+
Content-Type: application/json
+Authorization: Bearer [API key]
+
+

Documentation

+

The OpenCTI API consists of various endpoints corresponding to different functionalities and operations within the platform. These endpoints allow users to perform actions such as querying data, creating or updating entities, and more. Users can refer to the Understand GraphQL section to understand how it works.

+

Documentation for the OpenCTI API, including schema definitions, the list of filters available and queryable fields, is available through the OpenCTI platform. It can be found on the GraphQL playground. However, query examples and mutation examples are not yet available. In the meantime, users can explore the available endpoints and their functionality by inspecting network traffic in the browser's developer tools or by examining the source code of the Python client.

+

GraphQL query

+

+

Understand GraphQL

+

GraphQL is a powerful query language for APIs that enables clients to request exactly the data they need. Unlike traditional REST APIs, which expose fixed endpoints and return predefined data structures, GraphQL APIs allow clients to specify the shape and structure of the data they require.

+

Core concepts

+

Schema Definition Language (SDL)

+

GraphQL APIs are defined by a schema, which describes the types of data that can be queried and the relationships between them. The schema is written using the Schema Definition Language (SDL), which defines types, fields, and their relationships.

+

Query language

+

GraphQL uses a query language to request data from the server. Clients can specify exactly which fields they need and how they are related, enabling precise data retrieval without over-fetching or under-fetching.

+

Example:

+
query IntrusionSets {
+    intrusionSets (
+        filters:
+        {
+            mode: and,
+            filters: [
+                {
+                    key: "name",
+                    values: ["Ajax Security Team"],
+                    operator: eq,
+                    mode: or,
+                }
+            ],
+            filterGroups: [],
+        }
+    )
+    {
+        edges{
+            node{
+                id
+                name
+                description
+                externalReferences{
+                    edges{
+                        node{
+                            url
+                            source_name
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+

Resolvers

+

Resolvers are functions responsible for fetching the requested data. Each field in the GraphQL schema corresponds to a resolver function, which determines how to retrieve the data from the underlying data sources.

+

How it Works

+
    +
  1. Schema definition: The API provider defines a GraphQL schema using SDL, specifying the types and fields available for querying.
  2. +
  3. Query execution: Clients send GraphQL queries to the server, specifying the data they need. The server processes the query, resolves each field, and constructs a response object with the requested data.
  4. +
  5. Validation and execution: The server validates the query against the schema to ensure it is syntactically and semantically correct. If validation passes, the server executes the query, invoking the appropriate resolver functions to fetch the requested data.
  6. +
  7. Data retrieval: Resolvers fetch data from the relevant data sources, such as databases, APIs, or other services. They transform the raw data into the shape specified in the query and return it to the client.
  8. +
  9. Response formation: Once all resolvers have completed, the server assembles the response object containing the requested data and sends it back to the client.
  10. +
+

External Resources

+

For a more in-depth understanding of GraphQL and its usage, consider exploring the following external resources:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/assets/Create_a_TAXII_ingester.png b/6.2.X/reference/assets/Create_a_TAXII_ingester.png new file mode 100755 index 00000000..f0f6ff0a Binary files /dev/null and b/6.2.X/reference/assets/Create_a_TAXII_ingester.png differ diff --git a/6.2.X/reference/assets/TAXII_Feeds_panel.png b/6.2.X/reference/assets/TAXII_Feeds_panel.png new file mode 100755 index 00000000..2345a038 Binary files /dev/null and b/6.2.X/reference/assets/TAXII_Feeds_panel.png differ diff --git a/6.2.X/reference/assets/create_csv_feed.png b/6.2.X/reference/assets/create_csv_feed.png new file mode 100755 index 00000000..ae3f7c29 Binary files /dev/null and b/6.2.X/reference/assets/create_csv_feed.png differ diff --git a/6.2.X/reference/assets/csv_feed_distribution.png b/6.2.X/reference/assets/csv_feed_distribution.png new file mode 100755 index 00000000..f5855f22 Binary files /dev/null and b/6.2.X/reference/assets/csv_feed_distribution.png differ diff --git a/6.2.X/reference/assets/csv_feed_filters.png b/6.2.X/reference/assets/csv_feed_filters.png new file mode 100755 index 00000000..0267ebc8 Binary files /dev/null and b/6.2.X/reference/assets/csv_feed_filters.png differ diff --git a/6.2.X/reference/assets/csv_feed_generation.png b/6.2.X/reference/assets/csv_feed_generation.png new file mode 100755 index 00000000..5b1cc92f Binary files /dev/null and b/6.2.X/reference/assets/csv_feed_generation.png differ diff --git a/6.2.X/reference/assets/csv_feed_result.png b/6.2.X/reference/assets/csv_feed_result.png new file mode 100755 index 00000000..db2dff49 Binary files /dev/null and b/6.2.X/reference/assets/csv_feed_result.png differ diff --git a/6.2.X/reference/assets/filters-migration-example1.png b/6.2.X/reference/assets/filters-migration-example1.png new file mode 100755 index 00000000..10d6b202 Binary files /dev/null and b/6.2.X/reference/assets/filters-migration-example1.png differ diff --git a/6.2.X/reference/assets/filters-migration-url-warning.png b/6.2.X/reference/assets/filters-migration-url-warning.png new file mode 100755 index 00000000..afa6bc54 Binary files /dev/null and b/6.2.X/reference/assets/filters-migration-url-warning.png differ diff --git a/6.2.X/reference/assets/find_csv_feed_feature.png b/6.2.X/reference/assets/find_csv_feed_feature.png new file mode 100755 index 00000000..6cef8bdb Binary files /dev/null and b/6.2.X/reference/assets/find_csv_feed_feature.png differ diff --git a/6.2.X/reference/assets/graphql_query.png b/6.2.X/reference/assets/graphql_query.png new file mode 100755 index 00000000..a6331d5c Binary files /dev/null and b/6.2.X/reference/assets/graphql_query.png differ diff --git a/6.2.X/reference/assets/vocabularies-customization.png b/6.2.X/reference/assets/vocabularies-customization.png new file mode 100755 index 00000000..227267ed Binary files /dev/null and b/6.2.X/reference/assets/vocabularies-customization.png differ diff --git a/6.2.X/reference/assets/vocabularies-list.png b/6.2.X/reference/assets/vocabularies-list.png new file mode 100755 index 00000000..069b8a5c Binary files /dev/null and b/6.2.X/reference/assets/vocabularies-list.png differ diff --git a/6.2.X/reference/data-intelligence/index.html b/6.2.X/reference/data-intelligence/index.html new file mode 100755 index 00000000..6dece826 --- /dev/null +++ b/6.2.X/reference/data-intelligence/index.html @@ -0,0 +1,5048 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data Intelligence - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Data intelligence

+

As a cyber threat intelligence platform, OpenCTI offers functionalities that enable users to move quickly from raw data to operational intelligence by building up high-quality, structured information.

+

To do so, the platform provides a number of essential capabilities, such as automated data deduplication, merging of similar entities while preserving relationship integrity, the ability to modulate the confidence levels on your intelligence, and the presence of inference rules to automate the creation of logical relationships among your data.

+

The purpose of this page is to list the features of the platform that contribute to the intelligibility and quality of intelligence.

+

Deduplication

+

The first essential data intelligence mechanism in OpenCTI is the deduplication of information and relations.

+

This advanced functionality not only enables you to check whether a piece of data, information or a relationship is not a duplicate of an existing element, but will also, under certain conditions, enrich the element already present.

+

If the new duplicated entity has new content, the pre-existing entity can be enriched with the new information from the duplicate.

+

It works as follows (see details in the dedicated page):

+
    +
  • For entities: based on the entity's properties based on a specific ID generated by the "ID Contributing Properties" platform (properties listed on the dedicated page).
  • +
  • For relationships: based on type, source, target, start time, and stop time.
  • +
  • For observables: a specific ID is also generated by the platform, this time based on the specifications of the STIX model.
  • +
+

The ability to update and enrich is determined by the confidence level and quality level of the entities and relationships (see diagram on page deduplication).

+

Merging

+

OpenCTI's merging function is one of the platform's crucial data intelligence elements.

+

From the Data > Entities tab, this feature lets you merge up to 4 entities of the same type. A parent entity is selected and assigned up to three child entities.

+

The benefit of this feature is to centralize a number of similar elements from different sources without losing data or degrading the quality of the information. During merging, the platform will create relationships to anchor all the data to the consolidated entity.

+

This enrichment function consolidates the data and avoids duplication, but above all initiates a structured intelligence process while preserving the integrity of pre-existing relationships as presented here.

+

Confidence level and data segregation

+

Another key element of OpenCTI's data intelligence is its ability to apply confidence levels and to segregate the data present in the platform.

+

The confidence level is directly linked to users and Role Based Access Control. It is applied to a user directly or indirectly via the confidence level of the group to which the user belongs. This element is fundamental as it defines the levels of data manipulation to which the user (real or connector) is entitled.

+

The correct application of confidence levels is all the more important as it will determine the confidence level of the data manipulated by a user. It is therefore a decisive mechanism, since it underpins the confidence you have in the content of your instance.

+

While it is important to apply a level of trust to your users or groups, it is also important to define a way of categorizing and protecting your data.

+

Data segregation makes it possible to apply marking definitions and therefore establish a standardized framework for classifying data.

+

These marking definitions, like the classic Traffic Light Protocols (TLP) implemented by default in the platform, will determine whether a user can access a specific data set. The marking will be applied at the group level to which the user belongs, which will determine the data to which the user has access and therefore the data that the user can potentially handle.

+

Inference rules

+

In OpenCTI, data intelligence is not just about the ability to segregate, qualify or enrich data. OpenCTI's inference rules enable you to mobilize the data on your platform effectively and operationally.

+

These predefined rules enable the user to speed up cyber threat management. For example, inferences can be used to automatically identify incidents based on a sighting, to create sightings on observables based on new observed data, to propagate relationships based on an observable, etc.

+

In all, the platform includes some twenty high-performance inference rules that considerably speed up the analysis and response to threats (see the full list here).

+

These rules are based on a logical interpretation of the data, resulting in a pre-analysis of the information by creating relationships that will enrich the intelligence in the platform. There are three main benefits: efficiency, completeness and accuracy. These user benefits can be found here.

+

Note: If these rules are present in the platform, they are not activated by default.

+

Once activated, they scan all the data in your platform in the background to identify all the existing relationships that meet the conditions of the rules. Then, the rules operate continuously to create relationships. If you deactivate a rule, all the objects and relationships it has created will be deleted.

+

These actions can only be carried out by an administrator of the instance.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/data-model/index.html b/6.2.X/reference/data-model/index.html new file mode 100755 index 00000000..72b852e5 --- /dev/null +++ b/6.2.X/reference/data-model/index.html @@ -0,0 +1,4886 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data model - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Data model

+
+

Under construction

+

This page will be automatically generated to reference the platform's data model. We are doing our best to implement this automatic generation as quickly as possible.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/filters-migration/index.html b/6.2.X/reference/filters-migration/index.html new file mode 100755 index 00000000..299ccb2a --- /dev/null +++ b/6.2.X/reference/filters-migration/index.html @@ -0,0 +1,5450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Filter migration for 5.12 - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Filters format migration for OpenCTI 5.12

+

The version 5.12 of OpenCTI introduces breaking changes to the filters format used in the API. This documentation describes how you can migrate your scripts or programs that call the OpenCTI API, when updating from a version of OpenCTI inferior to 5.12.

+

Why this migration?

+

Before OpenCTI 5.12, it was not possible to construct complex filters combinations: we couldn't embed filters within filters, used different boolean modes (and/or), filter on all available attributes or relations for a given entity type, or even test for empty fields of any sort.

+

Legacy of years of development, the former format and filtering mechanics were not adapted for such task, and a profound refactoring was necessary to make it happen.

+

Here are the main pain points we identified beforehand:

+
    +
  • The filters frontend and backend formats were very different, requiring careful conversions.
  • +
  • The filter were static lists of keys, depending on each given entity type and maintained by hands.
  • +
  • The operator (eq, not_eq, etc.) was inside the key (e.g. entity_type_not_eq), limiting operator combination and requiring error-prone parsing.
  • +
  • The frontend format imposed a unique form of combination (and between filters, or between values inside each filter, and nothing else possible).
  • +
  • The flat list structure made impossible filter imbrication by nature.
  • +
  • Filters and query options were mixed in GQL queries for the same purpose (for instance, option types analog to a filter on key entity_type).
  • +
+
// filter formats in OpenCTI < 5.12
+
+type Filter = {
+    key: string, // a key in the list of the available filter keys for given the entity type
+    values: string[],
+    operator: string,
+    filterMode: 'and' | 'or',
+}
+
+// "give me Reports labelled with labelX or labelY"
+const filters = [
+  { 
+    "key": "entity_type",
+    "values": ["Report"],
+    "operator": "eq",
+    "filterMode": "or"
+  },
+  { 
+    "key": "labelledBy",
+    "values": ["<id-for-labelX>", "<id-for-labelY>"],
+    "operator": "eq",
+    "filterMode": "or"
+  },
+]
+
+

The new format brings a lot of short-term benefits and is compatible with our long-term vision of the filtering capabilities in OpenCTI. We chose a simple recursive structure that allow complex combination of any sort with respect to basic boolean logic.

+

The list of operator is fixed and can be extended during future developments.

+
// filter formats in OpenCTI >= 5.12
+
+type FilterGroup = {
+  mode: 'and' | 'or'
+  filters: Filter[]
+  filterGroups: FilterGroup[] // recursive definition
+}
+
+type Filter  = {
+  key: string[]
+  values: string[]
+  operator: 'eq' | 'not_eq' | 'gt' // ... and more
+  mode: 'and' | 'or',
+}
+
+// "give me Reports and RFIs, not marked TLP:RED with labelX or no label"
+const filters = {
+  mode: 'and',
+  filters: [
+    { key: 'entity_type', values: ['Report', 'Case-Rfi'], operator: 'eq', mode: 'or', },
+    { key: 'objectMarking', values: ['<id-for-TLP:RED>'], operator: 'not_eq', mode: 'or', },
+  ],
+  filterGroups: [{
+    mode: 'or',
+    filters: [
+      { key: 'objectLabel', values: ["<id-for-labelX>"], operator: 'eq', mode: 'or', },
+      { key: 'objectLabel', values: [], operator: 'nil', mode: 'or', },
+    ],
+    filterGroups: [],
+  }],
+};
+
+

Because changing filters format impacts almost everything in the platform, we decided to do a complete refactoring once and for all. We want this migration process to be clear and easy.

+

What has been changed

+

The new filter implementation bring major changes in the way filters are processed and executed.

+
    +
  • +

    We change the filters formats (see FilterGroup type above):

    +
      +
    • In the frontend, an operator and a mode are stored for each key.
    • +
    • The new format enables filters imbrication thanks to the new attribute 'filterGroups'.
    • +
    • The keys are of type string (no more static list of enums).
    • +
    • The 'values' attribute can no longer contain null values (use the nil operator instead).
    • +
    +
  • +
  • +

    We also renamed some filter keys, to be consistent with the entities schema definitions.

    +
  • +
  • We implemented the handling of the different operators and modes in the backend.
  • +
  • We introduced new void operators (nil / not_nil) to test the presence or absence of value in any field.
  • +
+

How to migrate your own filters

+

We wrote a migration script to convert all stored filters created prior to version 5.12. These filters will thus be migrated automatically when starting your updated platform.

+

However, you might have your own connectors, queries, or python scripts that use the graphql API or the python client. If this is the case, you must change the filter format if you want to run the code against OpenCTI >= 5.12.

+

Filter conversion

+

To convert filters prior to version 5.12 in the new format:

+
    +
  1. update the key field if it has been changed in 5.12 (see the conversion table below),
  2. +
  3. rename the field filterMode in mode
  4. +
  5. if values contains null, see below for conversion.
  6. +
+

Now you can build your new FilterGroup object with:

+
    +
  • mode: 'and'
  • +
  • filters = array of converted filters (following previous steps),
  • +
  • filterGroups: []
  • +
+
const oldFilter = {
+    key: 'old_key',
+    values: ['value1', 'value2'],
+    operator: 'XX',
+    filterMode: 'XX',
+}
+
+const convertedFilter = {
+    key: 'converted_key_if_necessary',
+    values: ['value1', 'value2'],
+    operator: 'XX',
+    mode: 'XX',
+}
+
+const newFilters = {
+    mode: 'and',
+    filters: [convertedFilter1, convertedFilter2...], // array with all converted filters
+    filterGroups: [],
+}
+
+

If values contains a null value, you need to convert the filter by using the new nil / not_nil operators. Here's the procedure:

+
    +
  1. +

    Extract one filter dedicated to null

    +
      +
    • if operator was 'eq', switch to operator: 'nil' / if operator was not_eq, switch to operator = 'not_nil'
    • +
    • values = []
    • +
    +
  2. +
  3. +

    Extract another filter for all the other values.

    +
  4. +
+
// "Must have a label that is not Label1 or Label2"
+const oldFilter = {
+    key: 'labelledBy',
+    values: [null, 'id-for-Label1', 'id-for-Label2'],
+    operator: 'not_eq',
+    filterMode: 'and',
+}
+
+const newFilters = {
+    mode: 'and',
+    filters: [
+      {
+        key: 'objectLabel',
+        values: ['id-label-1', 'id-for-Label2'],
+        operator: 'not_eq',
+        mode: 'and',
+      },
+      {
+        key: 'objectLabel',
+        values: [],
+        operator: 'not_nil',
+        mode: 'and',
+      },
+    ],
+    filterGroups: [],
+}
+
+
+

Switch to nested filter to preserve logic

+

To preserve the logic of your old filter you might need to compose nested filter groups. This could happen for instance when using eq operator with null values for one filter, combined in and mode with other filters.

+
+

+

Filter keys conversion table

+

Make sure you address all the filter keys that require conversion, following the map below:

+
// array of [oldKey, newKey] for the renamed keys
+const keyConversion = [
+    ['labelledBy', 'objectLabel'],
+    ['markedBy', 'objectMarking'],
+    ['objectContains', 'objects'],
+    ['killChainPhase', 'killChainPhases'],
+    ['assigneeTo', 'objectAssignee'],
+    ['participant', 'objectParticipant'],
+    ['creator', 'creator_id'],
+    ['hasExternalReference', 'externalReferences'],
+    ['hashes_MD5', 'hashes.MD5'],
+    ['hashes_SHA1', 'hashes.SHA-1'],
+    ['hashes_SHA256', 'hashes.SHA-256'],
+    ['hashes_SHA512', 'hashes.SHA-512']
+]
+
+

Examples

+

Simple example

+

Let's start with a simple case:

+
+

All Reports with label "label1" or "label2"

+

(entity_type = Report) AND (label = label1 OR label2)

+
+
const oldBackendFilter = [
+  {
+    key: 'entity_type',
+    values: ['Report'],
+    operator: 'eq',
+    filterMode: 'or',
+  },
+  {
+   key: 'labelledBy',
+    values: [label1_id, label2_id],
+    operator: 'eq',
+    filterMode: 'or',
+  },
+];
+
+const newFilters = {
+  mode: 'and',
+  filters: [
+    {
+      key: 'entity_type',
+      values: ['Report'],
+      operator: 'eq',
+      mode: 'or',
+    },
+    {
+      key: 'objectLabel',
+      values: [label1_id, label2_id],
+      operator: 'eq',
+      mode: 'or',
+    }
+  ],
+  filterGroups: [],
+};
+
+

Complex example

+

Now let's see a more complex case involving filter group nesting:

+
+

"All Reports that have either the label "label1" or "label2", or no label at all"

+

(entity_type = Report) AND (label = "No label" OR label1 OR label2)

+
+
const oldBackendFilter = [
+    {
+      key: 'labelledBy',
+      values: [label1_id, label2_id, null],
+      operator: 'eq',
+      filterMode: 'or',
+    },
+    {
+      key: 'entity_type',
+      values: ['Report'],
+      operator: 'eq',
+      filterMode: 'or',
+    },
+];
+
+const newFilters = {
+    mode: 'and',
+    filters: [
+      {
+        key: 'entity_type',
+        values: ['Report'],
+        operator: 'eq',
+        mode: 'or',
+      },
+    ],
+    // the combination mode/operator in this case requires nested filter groups
+    filterGroups: [
+      {
+        mode: 'or',
+        filters: [
+          {
+            key: 'objectLabel',
+            values: [label1_id, label2_id],
+            operator: 'eq',
+            mode: 'or',
+          },
+          {
+            key: 'objectLabel',
+            operator: 'nil',
+            values: [], // empty, does not matter
+            mode: 'or', // and/or, does not matter 
+          },
+        ],
+        filterGroups: [],
+      }
+    ],
+};
+
+

Residual dynamic filters

+

Dynamic filters are not stored in the database, they enable to filter view in the UI, e.g. filters in entities list, investigations, knowledge graphs. They are saved as URL parameters, and can be saved in local storage.

+

These filters are not migrated automatically and are lost when moving to 5.12. This concerns the filters saved for each view, that are restored when coming back to the same view. You will need to reconstruct the filters by hand in the UI; these new filters will be properly saved and restored afterward.

+

Also, when going to an url with filters in the old format, OpenCTI will display a warning and remove the filter parameters. Only URLs built by OpenCTI 5.12 are compatible with it, so you will need to reconstruct the filters by hand and save / share your updated links.

+

URL warning

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/filters/index.html b/6.2.X/reference/filters/index.html new file mode 100755 index 00000000..9d8abd5c --- /dev/null +++ b/6.2.X/reference/filters/index.html @@ -0,0 +1,5569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Filters knowledge - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Filter knowledge

+

In OpenCTI, you can filter data to target or view entities that have particular attributes.

+

Filters usages

+

There are two types of filters that are used in many locations in the platform:

+
    +
  • in entities lists: to display only the entities matching the filters. If an export or a background task is generated, only the filtered data will be taken into account,
  • +
  • in investigations and knowledge graphs: to display only the entities matching the filters,
  • +
  • in dashboards: to create widget with only the entities matching the filters,
  • +
  • in feeds, TAXII collections, triggers, streams, playbooks, background tasks: to process only the data or events matching the filters.
  • +
+

Dynamic filters

+

Dynamic filters are not stored in the database, they enable to filter view in the UI, e.g. filters in entities list, investigations, knowledge graphs.

+

However, they are still persistent in the platform frontend side. The filters used in a view are saved as URL parameters, so you can save and share links of these filtered views.

+

Also, your web browser saves in local storage the filters that you are setting in various places of the platform, allowing to retrieve them when you come back to the same view. You can then keep working from where you left of.

+

Filtering entities

+

+

Stored filters

+

Stored filters are attributes of an entity, and are therefore stored in the database. They are stored as an attribute in the object itself, e.g. filters in dashboards, feeds, TAXII collections, triggers, streams, playbooks.

+

Create a filter

+

To create a filter, add every key you need using the 'Add filter' select box. It will give you the possible attributes on which you can filter in the current view.

+

A grey box appears and allows to select:

+
    +
  1. the operator to use, and
  2. +
  3. the values to compare (if the operator is not "empty" or "not_empty").
  4. +
+

You can add as many filters as you want, even use the same key twice with different operators and values.

+

The boolean modes (and/or) are either global (between every attribute filters) or local (between values inside a filter). Both can be switched with a single click, changing the logic of your filtering.

+

Filters format

+

Since OpenCTI 5.12, the OpenCTI platform uses a new filter format called FilterGroup. The FilterGroup model enables to do complex filters imbrication with different boolean operators, which extends greatly the filtering capabilities in every part of the platform.

+

Structure

+

The new format used internally by OpenCTI and exposed through its API, can be described as below:

+
// filter formats in OpenCTI >= 5.12
+
+type FilterGroup = {
+  mode: 'and' | 'or'
+  filters: Filter[]
+  filterGroups: FilterGroup[] // recursive definition
+}
+
+type Filter  = {
+  key: string[] // or single string (input coercion)
+  values: string[]
+  operator: 'eq' | 'not_eq' | 'gt' // ... and more
+  mode: 'and' | 'or',
+}
+
+

Properties

+

In a given filter group, the mode (and or or) represents the boolean operation between the different filters and filterGroups arrays. The filters and filterGroups arrays are composed of objects of type Filter and FilterGroup.

+

The Filter has 4 properties:

+
    +
  • a key, representing the kind of data we want to target (example: objectLabel to filter on labels or createdBy to filter on the author),
  • +
  • an array of values, representing the values we want to compare to,
  • +
  • an operator representing the operation we want to apply between the key and the values,
  • +
  • a mode (and or or) to apply between the values if there are several ones.
  • +
+

Operators

+

The available operators are:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ValueMeaningAdditional information
eqequal
not_eqdifferent
glgreater thanagainst textual values, the alphabetical ordering is used
gtegreater than or equalagainst textual values, the alphabetical ordering is used
ltlower thanagainst textual values, the alphabetical ordering is used
ltelower than or equalagainst textual values, the alphabetical ordering is used
nilempty / no valuenil do not require anything inside values
not_nilnon-empty / any valuenot_nil do not require anything inside values
+

In addition, there are operators:

+
    +
  • starts_with / not_starts_with / ends_with / not_ends_with / contains / not contains, available for searching in short string fields (name, value, title, etc.),
  • +
  • search, available in short string and text fields.
  • +
+

There is a small difference between search and contains. search finds any occurrence of specified words, regardless of order, while "contains" specifically looks for the exact sequence of words you provide.

+
+

Always use single-key filters

+

Multi-key filters are not supported across the platform and are reserved to specific, internal cases.

+
+

Examples

+

entity_type = 'Report'

+
filters = {
+    mode: 'and',
+    filters: [{
+        key: 'entity_type',
+        values: ['Report'],
+        operator: 'eq',
+        mode: 'or', // useless here because values contains only one value
+    }],
+    filterGroups: [],
+};
+
+

(entity_type = 'Report') AND (label = 'label1' OR 'label2')

+
filters = {
+    mode: 'and',
+    filters: [
+        {
+            key: 'entity_type',
+            values: ['Report'],
+            operator: 'eq',
+            mode: 'or',
+        },
+        {
+            key: 'objectLabel',
+            values: ['label1', 'label2'],
+            operator: 'eq',
+            mode: 'or',
+        }
+    ],
+    filterGroups: [],
+};
+
+

(entity_type = 'Report') AND (confidence > 50 OR marking is empty)

+
filters = {
+    mode: 'and',
+    filters: [
+        {
+            key: 'entity_type',
+            values: ['Report'],
+            operator: 'eq',
+            mode: 'or',
+        }
+    ],
+    filterGroups: [
+        {
+            mode: 'or',
+            filters: [
+                {
+                    key: 'confidence',
+                    values: ['50'],
+                    operator: 'gt',
+                    mode: 'or',
+                },
+                {
+                    key: 'objectMarking',
+                    values: [],
+                    operator: 'nil',
+                    mode: 'or',
+                },
+            ],
+            filterGroups: [],
+        }
+    ],
+};
+
+

Keys

+

Filter keys validation

+

Only a specific set of key can be used in the filters.

+

Automatic key checking prevents typing error when constructing filters via the API. If a user write an unhandled key (object-label instead of objectLabel for instance), the API will return an error instead of an empty list. Doing so, we make sure the platform do not provide misleading results.

+

Regular filter keys

+

For an extensive list of available filter keys, refer to the attributes and relations schema definitions.

+

Here are some of the most useful keys as example. NB: X refers here to the filtered entities.

+
    +
  • objectLabel: label applied on X,
  • +
  • objectMarking: marking applied on X,
  • +
  • createdBy: author of X,
  • +
  • creator_id: technical creator of X,
  • +
  • created_at: date of creation of X in the platform,
  • +
  • confidence: confidence of X,
  • +
  • entity_type: entity type of X ('Report', 'Stix-Cyber-Observable', ...),
  • +
+

Special filter keys

+

Some keys do not exist in the schema definition, but are allowed in addition. They describe a special behavior.

+

It is the case for:

+
    +
  • sightedBy: entities to which X is linked via a STIX sighting relationship,
  • +
  • workflow_id: status id of the entities, or status template id of the status of the entities,
  • +
  • representative: entities whose representative (name for reports, value for some observables, composition of the source and target names for a relationship...) matches the filter,
  • +
  • connectedToId: the listened instances for an instance trigger.
  • +
+

For some keys, negative equality filtering is not supported yet (not_eq operator). For instance, it is the case for:

+
    +
  • fromId
  • +
  • fromTypes
  • +
  • toId
  • +
  • toTypes
  • +
+

The regardingOf filter key has a special format and enables to target the entities having a relationship of a certain type with certain entities. Here is an example of filter to fetch the entities related to the entity X:

+
filters = {
+  mode: 'and',
+  filters: [
+    {
+      key: 'regardingOf',
+      values: [
+        { key: 'id', values: ['id-of-X'] },
+        { key: 'relationship_type', values: ['related-to'] },
+      ],
+    },
+  ],
+  filterGroups: [],
+};
+
+

Limited support in stream events filtering

+

Filters that are run against the event stream are not using the complete schema definition in terms of filtering keys.

+

This concerns:

+
    +
  • Live streams,
  • +
  • CSV feeds,
  • +
  • TAXII collection,
  • +
  • Triggers,
  • +
  • Playbooks.
  • +
+

For filters used in this context, only some keys are supported for the moment:

+
    +
  • confidence
  • +
  • objectAssignee
  • +
  • createdBy
  • +
  • creator
  • +
  • x_opencti_detection
  • +
  • indicator_types
  • +
  • objectLabel
  • +
  • x_opencti_main_observable_type
  • +
  • objectMarking
  • +
  • objects
  • +
  • pattern_type
  • +
  • priority
  • +
  • revoked
  • +
  • severity
  • +
  • x_opencti_score
  • +
  • entity_type
  • +
  • x_opencti_workflow_id
  • +
  • connectedToId (for the instance triggers)
  • +
  • fromId (the instance in the "from" of a relationship)
  • +
  • fromTypes (the entity type in the "from" of a relationship)
  • +
  • toId (the instance in the "to" of a relationship)
  • +
  • toTypes (the entity type in the "to" of a relationship)
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/fips/index.html b/6.2.X/reference/fips/index.html new file mode 100755 index 00000000..b11c5cb4 --- /dev/null +++ b/6.2.X/reference/fips/index.html @@ -0,0 +1,5215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FIPS 140-2 installation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

SSL FIPS 140-2 deployment

+

Introduction

+

For organizations that need to deploy OpenCTI in a SSL FIPS 140-2 compliant environment, we provide FIPS compliant OpenCTI images for all components of the platform. Please note that you will also need to deploy dependencies (ElasticSearch / OpenSearch, Redis, etc.) with FIPS 140-2 SSL to have the full compliant OpenCTI technological stack.

+
+

OpenCTI SSL FIPS 140-2 compliant builds

+

The OpenCTI platform, workers and connectors SSL FIPS 140-2 compliant images are based on packaged Alpine Linux with OpenSSL 3 and FIPS mode enabled maintened by the Filigran engineering team.

+
+

Dependencies

+

AWS Native Services in FedRAMP compliant environment

+

It is important to remind that OpenCTI is fully compatible with AWS native services and all dependencies are available in both FedRAMP Moderate (East / West) and FedRAMP High (GovCloud) scopes.

+
    +
  • Amazon OpenSearch Service (OpenSearch)
  • +
  • Amazon ElastiCache (Redis)
  • +
  • Amazon MQ (RabbitMQ)
  • +
  • Amazon Simple Storage Service (S3 bucket)
  • +
+

ElasticSearch / OpenSearch

+

ElasticSearch is known to be compatible with FIPS 140-2 SSL using the proper JVM. There is a comprehensive guide in the Elastic documentation.

+

Alternatively, please note that Elastic is also providing an ElasticSearch FedRAMP authorized cloud offering.

+

Redis

+

Redis does not provide FIPS 140-2 SSL compliant Docker images but supports very well custom tls-ciphersuites that can be configured to use the system FIPS 140-2 OpenSSL library.

+

Alternatively, you can use a Stunnel TLS endpoint to ensure encrypted communication between OpenCTI and Redis. There are a few examples available, here or here.

+

RabbitMQ

+

RabbitMQ does not provide FIPS 140-2 SSL compliant Docker images but, as Redis, supports custom cipher suites. Also, it is confirmed since RabbitMQ version 3.12.5, the associated Erlang build (> 26.1), supports FIPS mode on OpenSSL 3.

+

Alternatively, you can use a Stunnel TLS endpoint to ensure encrypted communication between OpenCTI and RabbitMQ.

+

S3 Bucket / MinIO

+

If you cannot use an S3 endpoint already deployed in your FIPS 140-2 SSL compliant environment, MinIO provides FIPS 140-2 SSL compliant Docker images which then are very easy to deploy within your environment.

+

OpenCTI stack

+

Platform

+

For the platform, we provide FIPS 140-2 SSL compliant Docker images. Just use the appropriate tag to ensure you are deploying the FIPS compliant version and follow the standard Docker deployment procedure.

+

Worker

+

For the worker, we provide FIPS 140-2 SSL compliant Docker images. Just use the appropriate tag to ensure you are deploying the FIPS compliant version and follow the standard Docker deployment procedure.

+

Connectors

+

All connectors have FIPS 140-2 SSL compliant Docker images. For each connector you need to deploy, please use the tag {version}-fips instead of {version} and follow the standard deployment procedure. An example is available on Docker Hub.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/streaming/index.html b/6.2.X/reference/streaming/index.html new file mode 100755 index 00000000..a0c15e28 --- /dev/null +++ b/6.2.X/reference/streaming/index.html @@ -0,0 +1,5389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data Streaming - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Data Streaming

+

Presentation

+

In order to provide a real time way to consume STIX CTI information, OpenCTI provides data events in a stream that can be consumed to react on creation, update, deletion and merge. This way of getting information out of OpenCTI is highly efficient and already use by some connectors.

+

Technology

+

Redis stream

+

OpenCTI is currently using REDIS Stream as the technical layer. Each time something is modified in the OpenCTI database, a specific event is added in the stream.

+

SSE protocol

+

In order to provide a really easy consuming protocol we decide to provide a SSE (https://fr.wikipedia.org/wiki/Server-sent_events) HTTP URL linked to the standard login system of OpenCTI. Any user with the correct access rights can open and access http://opencti_instance/stream, and open an SSE connection to start receiving live events. You can of course consume directly the stream in Redis, but you will have to manage access and rights directly.

+

Events format

+
id: {Event stream id} -> Like 1620249512318-0
+event: {Event type} -> create / update / delete
+data: { -> The complete event data
+    version -> The version number of the event
+    type -> The inner type of the event
+    scope -> The scope of the event [internal or external]
+    data: {STIX data} -> The STIX representation of the data.
+    message -> A simple string to easy understand the event
+    origin: {Data Origin} -> Complex object with different information about the origin of the event
+    context: {Event context} -> Complex object with meta information depending of the event type
+}
+
+

It can be used to consume the stream from this specific point.

+

STIX data

+

The current stix data representation is based on the STIX 2.1 format using extension mechanism. Please take a look to the STIX documentation for more information.

+

Create

+

It's simply the data created in STIX format.

+

Delete

+

It's simply the data in STIX format just before his deletion. You will also find the automated deletions in context due to automatic dependency management.

+
{
+  "context": {
+      "deletions": [{STIX data}]
+  }
+}
+
+

Update

+

This event type publish the complete STIX data information along with patches information. Thanks to the patches, it's possible to rebuild the previous version and easily understand what happens in the update.

+

Patch and reverse_patch follow the official jsonpatch specification. You can find more information on the jsonpatch page

+
{
+  "context": {
+      "patch": [/* patch operation object */],
+      "reverse_patch": [/* patch operation object */]
+  }
+}
+
+

Merge

+

Merge is a combination of an update of the merge targets and deletions of the sources. In this event you will find the same patch and reverse_patch as an update and the list of elements merged into the target in the "sources" attribute.

+
{
+  "context": {
+      "patch": [/* patch operation object */],
+      "reverse_patch": [/* patch operation object */],
+      "sources": [{STIX data}]
+  }
+}
+
+

Stream types

+

In OpenCTI we propose 2 types of streams.

+

Base stream

+

The stream hosted in /stream url contains all the raw events of the platform, always filtered by the user rights (marking based). It's a technical stream a bit complex to used but very useful for internal processing or some specific connectors like backup/restore.

+

This stream is live by default but if, you want to catchup, you can simply add the from parameter to your query. This parameter accept a timestamp in millisecond and also an event id, e.g. http://localhost/stream?from=1620249512599

+
+

Stream size?

+

The raw stream is really important in the platform and needs te be sized according to the period of retention you want to ensure. More retention you will have, more security about reprocessing the past information you will get. +We usually recommand 1 month of retention, that usually match 2 000 000 of events. This limit can be configured with redis:trimming option, please check deployment configuration page.

+
+

Live stream

+

This stream aims to simplify your usage of the stream through the connectors, providing a way to create stream with specific filters through the UI. After creating this stream, is simply accessible from /stream/{STREAM_ID}.

+

It's very useful for various cases of data externalization, synchronization, like SPLUNK, TANIUM...

+

This stream provides different interesting mechanics:

+
    +
  • Stream the initial list of instances matching your filters when connecting based on main database if you use the recover parameter
  • +
  • Auto dependencies resolution to guarantee the consistency of the information distributed
  • +
  • Automatic events translation depending on the element segregation
  • +
+

If you want to dig in about the internal behavior you can check this complete diagram:

+ + +

General options

+
    +
  • no-dependencies (query parameter or header, default false). Can be used to prevent the auto dependencies resolution. To be used with caution.
  • +
  • listen-delete (query parameter or header, default true). Can be used prevent receive deletion events. To be used with caution.
  • +
  • with-inferences (query parameter or header, default false). Can be used to add inferences events (from rule engine) in the stream.
  • +
+

From and Recover

+

From and recover are 2 different options that need to be explains.

+
    +
  • from (query parameter) is always the parameter that describe the initial date/event_id you want to start from. Can also be setup with request header from or last-event-id
  • +
  • recover (query parameter) is an option that let you consume the initial event from the database and not from the stream. Can also be setup with request header recover or recover-date
  • +
+

This difference will be transparent for the consumer but very important to get old information as an initial snapshot. This also let you consume information that is no longer in the stream retention period.

+

The next diagram will help you to understand the concept:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/taxonomy/index.html b/6.2.X/reference/taxonomy/index.html new file mode 100755 index 00000000..dfb27219 --- /dev/null +++ b/6.2.X/reference/taxonomy/index.html @@ -0,0 +1,5199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Taxonomy - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Taxonomy

+

In OpenCTI, taxonomies serve as structured classification systems that aid in organizing and categorizing intelligence data. This reference guide provides an exhaustive description of the platform's customizable fields within the taxonomies' framework. Users can modify, add, or delete values within the available vocabularies to tailor the classification system to their specific requirements.

+

For broader documentation on the taxonomies section, please consult the appropriate page.

+

Vocabularies

+

Default values are based on those defined in the STIX standard but can be tailored to better suit the organization's needs.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameUsed inDefault value
Account type vocabulary (account-type-ov)User accountfacebook, ldap, nis, openid, radius, skype, tacacs, twitter, unix, windows-domain, windows-local
Attack motivation vocabulary (attack-motivation-ov)Threat actor groupaccidental, coercion, dominance, ideology, notoriety, organizational-gain, personal-gain, personal-satisfaction, revenge, unpredictable
Attack resource level vocabulary (attack-resource-level-ov)Threat actor groupclub, contest, government, individual, organization, team
Case priority vocabulary (case_priority_ov)Incident responseP1, P2, P3, P4
Case severity vocabulary (case_severity_ov)Incident responsecritical, high, medium, low
Channel type vocabulary (channel_type_ov)ChannelFacebook, Twitter
Collection layers vocabulary (collection_layers_ov)Data sourcecloud-control-plane, container, host, network, OSINT
Event type vocabulary (event_type_ov)Eventconference, financial, holiday, international-summit, local-election, national-election, sport-competition
Eye color vocabulary (eye_color_ov)Threat actor individualblack, blue, brown, green, hazel, other
Gender vocabulary (gender_ov)Threat actor individualfemale, male, nonbinary, other
Grouping context vocabulary (grouping_context_ov)Groupingmalware-analysis, suspicious-activity, unspecified
Hair color vocabulary vocabulary (hair_color_ov)Threat actor individualbald, black, blond, blue, brown, gray, green, other, red
Implementation language vocabulary (implementation_language_ov)Malwareapplescript, bash, c, c++, c#, go, java, javascript, lua, objective-c, perl, php, powershell, python, ruby, scala, swift, typescript, visual-basic, x86-32, x86-64
Incident response type vocabulary (incident_response_type_ov)Incident responsedata-leak, ransomware
Incident severity vocabulary (incident_severity_ov)Incidentcritical, high, medium, low
Incident type vocabulary (incident_type_ov)Incidentalert, compromise, cybercrime, data-leak, information-system-disruption, phishing, reputation-damage, typosquatting
Indicator type vocabulary (indicator_type_ov)Indicatoranomalous-activity, anonymization, attribution, benign, compromised, malicious-activity, unknown
Infrastructure type vocabulary (infrastructure_type_ov)Infrastructureamplification, anonymization, botnet, command-and-control, control-system, exfiltration, firewall, hosting-malware, hosting-target-lists, phishing, reconnaissance, routers-switches, staging, unknown, workstation
Integrity level vocabulary (integrity_level_ov)Processhigh, medium, low, system
Malware capabilities vocabulary (malware_capabilities_ov)Malwareaccesses-remote-machines, anti-debugging, anti-disassembly, anti-emulation, anti-memory-forensics, anti-sandbox, anti-vm, captures-input-peripherals, captures-output-peripherals, captures-system-state-data, cleans-traces-of-infection, commits-fraud, communicates-with-c2, compromises-data-availability, compromises-data-integrity, compromises-system-availability, controls-local-machine, degrades-security-software, degrades-system-updates, determines-c2-server, emails-spam, escalates-privileges, evades-av, exfiltrates-data, fingerprints-host, hides-artifacts, hides-executing-code, infects-files, infects-remote-machines, installs-other-components, persists-after-system-reboot, prevents-artifact-access, prevents-artifact-deletion, probes-network-environment, self-modifies, steals-authentication-credentials, violates-system-operational-integrity
Malware result vocabulary (malware_result_ov)Malware analysisbenign, malicious, suspicious, unknown
Malware type vocabulary (malware_type_ov)Malwareadware, backdoor, bootkit, bot, ddos, downloader, dropper, exploit-kit, keylogger, ransomware, remote-access-trojan, resource-exploitation, rogue-security-software, rootkit, screen-capture, spyware, trojan, unknown, virus, webshell, wiper, worm
Marital status vocabulary (marital_status_ov)Threat actor individualannulled, divorced, domestic_partner, legally_separated, married, never_married, polygamous, separated, single, widowed
Note types vocabulary (note_types_ov)Noteanalysis, assessment, external, feedback, internal
Opinion vocabulary (opinion_ov)Opinionagree, disagree, neutral, strongly-agree, strongly-disagree
Pattern type vocabulary (pattern_type_ov)Indicatoreql, pcre, shodan, sigma, snort, spl, stix, suricata, tanium-signal, yara
Permissions vocabulary (permissions_ov)Attack patternAdministrator, root, User
Platforms vocabulary (platforms_ov)Data sourceandroid, Azure AD, Containers, Control Server, Data Historian, Engineering Workstation, Field Controller/RTU/PLC/IED, Google Workspace, Human-Machine Interface, IaaS, Input/Output Server, iOS, linux, macos, Office 365, PRE, SaaS, Safety Instrumented System/Protection Relay, windows
Processor architecture vocabulary (processor_architecture_ov)Malwarealpha, arm, ia-64, mips, powerpc, sparc, x86, x86-64
Reliability vocabulary (reliability_ov)Report, OrganizationA - Completely reliable, B - Usually reliable, C - Fairly reliable, D - Not usually reliable, E - Unreliable, F - Reliability cannot be judged
Report types vocabulary (report_types_ov)Reportinternal-report, threat-report
Request for information types vocabulary (request_for_information_types_ov)Request for informationnone
Request for takedown types vocabulary (request_for_takedown_types_ov)Request for takedownbrand-abuse, phishing
Service status vocabulary (service_status_ov)ProcessSERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED, SERVICE_RUNNING, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_STOPPED
Service type vocabulary (service_type_ov)ProcessSERVICE_FILE_SYSTEM_DRIVER, SERVICE_KERNEL_DRIVER, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS
Start type vocabulary (start_type_ov)ProcessSERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_SYSTEM_ALERT
Threat actor group role vocabulary (threat_actor_group_role_ov)Threat actor groupagent, director, independent, infrastructure-architect, infrastructure-operator, malware-author, sponsor
Threat actor group sophistication vocabulary (threat_actor_group_sophistication_ov)Threat actor groupadvanced, expert, innovator, intermediate, minimal, none, strategic
Threat actor group type vocabulary (threat_actor_group_type_ov)Threat actor groupactivist, competitor, crime-syndicate, criminal, hacker, insider-accidental, insider-disgruntled, nation-state, sensationalist, spy, terrorist, unknown
Threat actor individual role vocabulary (threat_actor_individual_role_ov)Threat actor individualagent, director, independent, infrastructure-architect, infrastructure-operator, malware-author, sponsor
Threat actor individual sophistication vocabulary (threat_actor_individual_sophistication_ov)Threat actor individualadvanced, expert, innovator, intermediate, minimal, none, strategic
Threat actor individual type vocabulary (threat_actor_individual_type_ov)Threat actor individualactivist, competitor, crime-syndicate, criminal, hacker, insider-accidental, insider-disgruntled, nation-state, sensationalist, spy, terrorist, unknown
Tool types vocabulary (tool_types_ov)Toolcredential-exploitation, denial-of-service, exploitation, information-gathering, network-capture, remote-access, unknown, vulnerability-scanning
+

Vocabularies list

+

Customization

+

Users can customize the taxonomies by modifying the available values or adding new ones. These modifications enable users to adapt the classification system to their specific intelligence requirements. Additionally, within each vocabulary list, users have the flexibility to customize the order of the dropdown menu associated with the taxonomy. This feature allows users to prioritize certain values or arrange them in a manner that aligns with their specific classification needs. Additionally, users can track the usage count for each vocabulary, providing insights into the frequency of usage and helping to identify the most relevant and impactful classifications. These customization options empower users to tailor the taxonomy system to their unique intelligence requirements, enhancing the efficiency and effectiveness of intelligence analysis within the OpenCTI platform.

+

Vocabularies customization

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/reference/usage-telemetry/index.html b/6.2.X/reference/usage-telemetry/index.html new file mode 100755 index 00000000..c95739d5 --- /dev/null +++ b/6.2.X/reference/usage-telemetry/index.html @@ -0,0 +1,5010 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Usage telemetry - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Usage telemetry

+

The application collects statistical data related to its usage and performances.

+
+

Confidentiality

+

The OpenCTI platform does not collect any information related to threat intelligence knowledge which remains strictly confidential. Also, the collection is strictly anonymous and personally identifiable information is NOT collected (including IP addresses).

+
+

All data collected is anonymized and aggregated to protect the privacy of individual users, in compliance with all privacy regulations.

+

Purpose of the telemetry

+

The collected data is used for the following purposes:

+
    +
  • Improving the functionality and performance of the application.
  • +
  • Analyzing user behavior to enhance user experience.
  • +
  • Generating aggregated and anonymized statistics for internal and external reporting.
  • +
+

Important thing to know

+

The platform send the metrics to the hostname telemetry.filigran.io using the OTLP protocol (over HTTPS). The format of the data is OpenTelemetry JSON.

+

The metrics push is done every 6 hours if OpenCTI was able to connect to the hostname when the telemetry manager is started. Metrics are also written in specific logs files in order to be included in support package

+

Telemetry metrics

+

The application collects statistical data related to its usage. Here are an exhaustive list of the collected metrics:

+
    +
  • The current platform version
  • +
  • The platform unique identifier
  • +
  • The platform creation date
  • +
  • The number of active users
  • +
  • The number of total users
  • +
  • The number of nodes in the platform
  • +
  • Enterprise Edition status (activated or not)
  • +
  • The number of active connectors
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/search/search_index.json b/6.2.X/search/search_index.json new file mode 100755 index 00000000..6ea8de2a --- /dev/null +++ b/6.2.X/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"OpenCTI Documentation Space","text":"

Welcome to the OpenCTI Documentation space. Here you will be able to find all documents, meeting notes and presentations about the platform.

Release notes

Please, be sure to also take a look at the OpenCTI releases notes, they may contain important information about releases and deployments.

"},{"location":"#introduction","title":"Introduction","text":"

OpenCTI is an open source platform allowing organizations to manage their cyber threat intelligence knowledge and observables. It has been created in order to structure, store, organize and visualize technical and non-technical information about cyber threats.

"},{"location":"#getting-started","title":"Getting started","text":"
  • Deployment & Setup

    Learn how to deploy and configure the platform as well as launch connectors to get the first data in OpenCTI.

    Deploy now

  • User Guide

    Understand how to use the platform, explore the knowledge, import and export information, create dashboard, etc.

    Explore

  • Administration

    Know how to administrate OpenCTI, create users and groups using RBAC / segregation, put retention policies and custom taxonomies.

    Customize

Need more help?

We are doing our best to keep this documentation complete, accurate and up to date.

If you still have questions or you find something which is not sufficiently explained, join the Filigran Community on Slack.

"},{"location":"#latest-blog-posts","title":"Latest blog posts","text":"

All tutorials are published directly on the Medium blog, this section provides a comprehensive list of the most important ones.

  • Introducing decay rules implementation for Indicators in OpenCTI Mar 25, 2024

    Cyber Threat Intelligence is made to be used. To be useful, it must be relevant and on time. It is why managing the lifecycle of Indicators of Compromise...

    Read

  • Introducing advanced filtering possibilities in OpenCTI Feb 5, 2024

    CTI databases are usually vast and made of complex, inter-dependent objects ingested from various sources. In this challenging context, cyber analysts need...

    Read

  • Breaking change: evolution of the way Connector, Streams and Feeds import data in OpenCTI Jan 29, 2024

    How Connectors, Feeds and Streams use Confidence level currently...

    Read

"},{"location":"#additional-resources","title":"Additional resources","text":"

Below, you will find external resources which may be useful along your OpenCTI journey.

OpenCTI Ecosystem List of available connectors and integrations to expand platform usage.

Training Courses Training courses for analysts and administrators in the Filigran training center.

Video materials Set of video illustrating the implementation of use cases and platform capabilities.

"},{"location":"administration/csv-mappers/","title":"CSV Mappers","text":"

In OpenCTI, CSV Mappers allow to parse CSV files in a STIX 2.1 Object. The mappers are created and configured by users with the Manage CSV mapper capability. Then, they are available to users who import CSV files, for instance inside a report or in the global import view.

"},{"location":"administration/csv-mappers/#principles","title":"Principles","text":"

The mapper contains representations of STIX 2.1 entities and relationships, in order for the parser to properly extract them. One mapper is dedicated to parsing a specific CSV file structure, and thus dedicated mappers should be created for every specific CSV structure you might need to ingest in the platform.

"},{"location":"administration/csv-mappers/#create-a-new-csv-mapper","title":"Create a new CSV Mapper","text":"

In menu Data, select the submenu Processing, and on the right menu select CSV Mappers. You are presented with a list of all the mappers set in the platform. Note that you can delete or update any mapper from the context menu via the burger button beside each mapper.

Click on the button + in the bottom-right corner to add a new Mapper.

Enter a name for your mapper and some basic information about your CSV files:

  • The line separator used (defaults to the standard comma character)
  • The presence of a header on the first line

Header management

The parser will not extract any information from the CSV header if any, it will just skip the first line during parsing.

Then, you need to create every representation, one per entity and relationship type represented in the CSV file. Click on the + button to add an empty representation in the list, and click on the chevron to expand the section and configure the representation.

Depending on the entity type, the form contains the fields that are either required (input outlined in red) or optional. For each field, set the corresponding columns mapping (the letter-based index of the column in the CSV table, as presented in common spreadsheet tools).

References to other entities should be picked from the list of all the other representations already defined earlier in the mapper.

You can do the same for all the relationships between entities that might be defined in this particular CSV file structure.

Fields might have options besides the mandatory column index, to help extract relevant data:

  • Date values are expected in ISO 8601 format, but you can set your own format to the time parser
  • Multiple values can be extracted by specifying the separator used inside the cell (e.g. + or |)

Or to set default values in case some data is missing in the imported file.

"},{"location":"administration/csv-mappers/#csv-mapper-validity","title":"CSV Mapper validity","text":"

The only parameter required to save a CSV Mapper is a name. The creation and refinement of its representations can be done iteratively.

Nonetheless, all CSV Mappers go through a quick validation that checks if all the representations have all their mandatory fields set. Only valid mappers can be run by the users on their CSV files.

Mapper validity is visible in the list of CSV Mappers as shown below.

"},{"location":"administration/csv-mappers/#test-your-csv-mapper","title":"Test your CSV mapper","text":"

In the creation or edition form, hit the button Test to open a dialog. Select a sample CSV file and hit the Test button.

The code block contains the raw result of the parsing attempt, in the form of a STIX 2.1 bundle in JSON format.

You can then check if the extracted values match the expected entities and relationships.

Partial test

The test conducted in this window relies only on the translation of CSV data according to the chosen representation in the mapper. It does not take into account checks for accurate entity formatting (e.g. IPv4) or specific entity configurations (e.g. mandatory \"description\" field on reports). Consequently, the entities visible in the test window may not be created during the actual import process.

Test with a small file

We strongly recommend limiting test files to 100 lines and 1MB. Otherwise, the browser may crash.

"},{"location":"administration/csv-mappers/#use-a-mapper-for-importing-a-csv-file","title":"Use a mapper for importing a CSV file","text":"

You can change the default configuration of the import csv connector in your configuration file.

\"import_csv_built_in_connector\": {\n  \"enabled\": true, \n  \"interval\": 10000, \n  \"validate_before_import\": false\n},\n

In Data import section, or Data tab of an entity, when you upload a CSV, you can select a mapper to apply to the file. The file will then be parsed following the representation rules set in the mapper.

By default, the imported elements will be added in a new Analyst Workbench where you will be able to check the result of the import.

"},{"location":"administration/csv-mappers/#default-values-for-attributes","title":"Default values for attributes","text":"

In the case of the CSV file misses some data, you can complete it with default values. To achieve this, you have two possibilities:

  • Set default values in the settings of the entities,
  • Set default values directly in the CSV mapper.
"},{"location":"administration/csv-mappers/#set-default-values-in-the-settings-of-the-entities","title":"Set default values in the settings of the entities","text":"

Default value mechanisms

Note that adding default values in settings have an impact at entity creation globally on the platform, not only on CSV mappers. If you want to apply those default values only at CSV mapper level, please use the second option.

In settings > Customization, you can select an entity type and then set default values for its attributes.

In the configuration of the entity, you have access to the entity's attributes that can be managed.

Click on the attribute to add a default value information.

Enter the default value in the input and save the update.

The value filled will be used in the case where the CSV file lacks data for this attribute.

"},{"location":"administration/csv-mappers/#set-specific-default-values-directly-in-the-csv-mapper","title":"Set specific default values directly in the CSV mapper","text":"

Information retained in case of default value

If you fill a default value in entity settings and the CSV mapper, the one from CSV mapper will be used.

In the mapper form, you will see next to the column index input a gear icon to add extra information for the attribute. If the attribute can have a customizable default value, then you will be able to set one here.

The example above shows the case of the attribute architecture implementation of a malware. You have some information here. First, it seems we have a default value already set in entity settings for this attribute with the value [powerpc, x86]. However, we want to override this value with another one for our case: [alpha].

"},{"location":"administration/csv-mappers/#specific-case-of-marking-definitions","title":"Specific case of marking definitions","text":"

For marking definitions, setting a default value is different from other attributes. We are not choosing a particular marking definition to use if none is specified in the CSV file. Instead, we will choose a default policy. Two option are available:

  • Use the default marking definitions of the user. In this case the default marking definitions of the connected user importing the CSV file will be used,
  • Let the user choose marking definitions. Here the user importing the CSV file will choose marking definitions (among the ones they can see) when selecting the CSV mapper.

"},{"location":"administration/csv-mappers/#additional-resources","title":"Additional resources","text":"
  • Usefulness: To additional content on entity customization, refers to the Customize entities page in the Administration section of the documentation.
"},{"location":"administration/decay-rules/","title":"Decay rules","text":"

Decay rules are used to update automatically indicators score in order to represent their lifecycle.

"},{"location":"administration/decay-rules/#configuration","title":"Configuration","text":"

Decay rules can be configured in the \"Settings > Customization > Decay rule\" menu.

There are built-in decay rules that can't be modified and are applied by default to indicators depending on their main observable type. Decay rules are applied from highest to lowest order (the lowest being 0).

You can create new decay rules with higher order to apply them along with (or instead of) the built-in rules.

When you create a decay rule, you can specify on which indicators' main observable types it will apply. If you don't enter any, it will apply to all indicators.

You can also add reaction points which represent the scores at which indicators are updated. For example, if you add one reaction point at 60 and another one at 40, indicators that have an initial score of 80 will be updated with a score of 60, then 40, depending on the decay curve.

The decay curve is based on two parameters:

  • the decay factor, which represents the speed at which the score falls, and
  • the lifetime, which represents the time (in days) during which the value will be lowered until it reaches 0.

Finally, the revoke score is the score at which the indicator can be revoked automatically.

Once you have created a new decay rule, you will be able to view its details, along with a life curve graph showing the score evolution over time.

You will also be able to edit your rule, change all its parameters and order, activate or deactivate it (only activated rules are applied), or delete it.

Indicator decay manager

Decay rules are only applied, and indicators score updated, if indicator decay manager is enabled (enabled by default).

Please read the dedicated page to have all information

"},{"location":"administration/enterprise/","title":"Enterprise edition","text":"

Filigran

Filigran is providing an Enterprise Edition of the platform, whether on-premise or in the SaaS.

"},{"location":"administration/enterprise/#what-is-opencti-ee","title":"What is OpenCTI EE?","text":"

OpenCTI Enterprise Edition is based on the open core concept. This means that the source code of OCTI EE remains open source and included in the main GitHub repository of the platform but is published under a specific license. As specified in the GitHub license file:

  • The OpenCTI Community Edition is licensed under the Apache License, Version 2.0 (the \u201cApache License\u201d).
  • The OpenCTI Enterprise Edition is licensed under the OpenCTI Enterprise Edition License (the \u201cEnterprise Edition Licensee\u201d).

The source files in this repository have a header indicating which license they are under. If no such header is provided, this means that the file belongs to the Community Edition under the Apache License, Version 2.0.

We wrote a complete article to explain the enterprise edition, feel free to read it to have more information

"},{"location":"administration/enterprise/#ee-activation","title":"EE Activation","text":"

Enterprise edition is easy to activate. You need to go the platform settings and click on the Activate button.

Then you will need to agree to the Filigran EULA.

As a reminder:

  • OpenCTI EE is free-to-use for development, testing and research purposes as well as for non-profit organizations.
  • OpenCTI EE is included for all Filigran SaaS customers without additional fee.
  • For all other usages, OpenCTI EE is reserved to organizations that have signed a Filigran Enterprise agreement.
"},{"location":"administration/enterprise/#available-features","title":"Available features","text":""},{"location":"administration/enterprise/#activity-monitoring","title":"Activity monitoring","text":"

Audit logs help you answer \"who did what, where, and when?\" within your data with the maximum level of transparency. Please read Activity monitoring page to get all information.

"},{"location":"administration/enterprise/#playbooks-and-automation","title":"Playbooks and automation","text":"

OpenCTI playbooks are flexible automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform. Please read Playbook automation page to get all information.

"},{"location":"administration/enterprise/#organizations-management-and-segregation","title":"Organizations management and segregation","text":"

Organizations segregation is a way to segregate your data considering the organization associated to the users. Useful when your platform aims to share data to multiple organizations that have access to the same OpenCTI platform. Please read Organizations RBAC to get more information.

"},{"location":"administration/enterprise/#full-text-indexing","title":"Full text indexing","text":"

Full text indexing grants improved searches across structured and unstructured data. OpenCTI classic searches are based on metadata fields (e.g. title, description, type) while advanced indexing capability enables searches to be extended to the document\u2019s contents. Please read File indexing to get all information.

"},{"location":"administration/enterprise/#more-to-come","title":"More to come","text":"

More features will be available in OpenCTI in the future. Features like:

  • Generative AI for correlation and content generation.
  • Supervised machine learning for natural language processing.
"},{"location":"administration/entities/","title":"Customize entities","text":""},{"location":"administration/entities/#introduction","title":"Introduction","text":"

A variety of entity customization options are available to optimize data representation, workflow management, and enhance overall user experience. Whether you're fine-tuning processing statuses, configuring entities' attributes, or hiding entities, OpenCTI's customization capabilities provide the flexibility you need to create a tailored environment for your threat intelligence and cybersecurity workflows.

The following chapter aims to provide readers with an understanding of the available customization options by entity type. Customize entities can be done in \"Settings > Customization\".

"},{"location":"administration/entities/#hidden-in-interface","title":"Hidden in interface","text":"

This configuration allows to hide a specific entity type throughout the entire platform. It provides a potent means to simplify the interface and tailor it to your domain expertise. For instance, if you have no interest in disinformation campaigns, you can conceal related entities such as Narratives and Channels from the menus.

You can specify which entities to hide on a platform-wide basis from \"Settings > Customization\" and from \"Settings > Parameters\", providing you with a list of hidden entities. Furthermore, you can designate hidden entities for specific Groups and Organizations from \"Settings > Security > Groups/Organizations\" by editing a Group/Organization.

An overview of hidden entity types is available in the \"Hidden entity types\" field in \"Settings > Parameters.\"

"},{"location":"administration/entities/#automatic-references-at-file-upload","title":"Automatic references at file upload","text":"

This configuration enables an entity to automatically construct an external reference from the uploaded file.

"},{"location":"administration/entities/#enforce-references","title":"Enforce references","text":"

This configuration enables the requirement of a reference message on an entity creation or modification. This option is helpful if you want to keep a strong consistency and traceability of your knowledge and is well suited for manual creation and update.

"},{"location":"administration/entities/#workflow","title":"Workflow","text":"

For now, OpenCTI has a simple workflow approach. They're represented by the \"Processing status\" field embedded in each object. By default, this field is disabled for most objects but can be activated through the platform settings:

  1. Navigate to \"Settings > Customization > Entity types > [Desired object type].\"
  2. Click on the small pink pen icon next to \"Workflow\" to access the object customization window.
  3. Add and configure the desired statuses, defining their order within the workflow.

In addition, the available statuses are defined by a collection of status templates visible in \"Settings > Taxonomies > Status templates\". This collection can be customized.

"},{"location":"administration/entities/#attributes","title":"Attributes","text":"

Each attribute in an Entity offers several customization options:

  • It can be set as mandatory if not already defined as such in the STIX standard.
  • A default value can be established to streamline entity creation through the creation forms.
  • Different thresholds and their corresponding labels for scalable attributes can be defined.

"},{"location":"administration/entities/#confidence-scale-configuration","title":"Confidence scale configuration","text":"

Confidence scale can be customized for each entity type by selecting another scale template or by editing directly the scale values. Once you have customized your scale, click on \"Update\" to save your configuration.

Max confidence level

The above scale also needs to take into account the confidence level per user. To understand the concept, please navigate to this page

"},{"location":"administration/file-indexing/","title":"File indexing","text":"

Enterprise edition

Platform segregation by organization is available under the \"OpenCTI Enterprise Edition\" license. Please read the dedicated page to have all the information.

"},{"location":"administration/file-indexing/#files-search-requirements","title":"Files search requirements","text":"

In order to search in files, we need to extract and index files text content, which requires to have one of these database configurations :

  • Elasticsearch >= 8.4
  • Elasticsearch < 8.4 with ingest-attachment plugin
  • OpenSearch with ingest-attachment plugin
"},{"location":"administration/file-indexing/#file-indexing-configuration","title":"File indexing configuration","text":"

File indexing can be configured via the File indexing tab in the Settings menu.

The configuration and impact panel shows all file types that can be indexed, as well as the volume of storage used.

It is also possible to include or exclude files uploaded from the global Data import panel and that are not associated with a specific entity in the platform.

Finally, it is possible to set a maximum file size for indexing (5 Mb by default).

Currently supported content types : application/pdf, text/plain, text/csv, text/html, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (excel sheets).

Once indexing has been launched by pressing the Start button, you can follow its progress.

You will also be able to Pause it and restart from the beginning by pressing Reset button which deletes all indexed files from the database.

If you change the configuration while file indexing is running, you might need to reset in order to include newly impacted files.

File indexing runs every 5 minutes to index last uploaded files.

"},{"location":"administration/introduction/","title":"Introduction","text":"

This guide aims to give you a full overview of the OpenCTI features and workflows. The platform can be used in various contexts to handle threats management use cases from a technical to a more strategic level.

"},{"location":"administration/introduction/#administrative-settings","title":"Administrative Settings","text":"

The OpenCTI Administrative settings console allows administrators to configure many options dynamically within the system. As an Administrator, you can access this settings console, by clicking the settings link.

The Settings Console allows for configuration of various aspects of the system.

"},{"location":"administration/introduction/#general-configuration","title":"General Configuration","text":"
  • Platform Title (Default: OpenCTI - Cyber Threat Intelligence Platform)
  • Platform Favicon
  • Platform General Sender email (Default: admin@opencti.io)
  • Platform Default Theme (Default: Dark)
  • Language (Default: Automatic Detection)
  • Hidden Entity Types (Default: None)
"},{"location":"administration/introduction/#authentication-strategies-display","title":"Authentication Strategies Display","text":"
  • This section will show configured and enabled/disabled strategies. The configuration is done in the config/default.json file or via ENV variables detected at launch.
"},{"location":"administration/introduction/#platform-messages","title":"Platform Messages","text":"
  • Platform Login Message (optional) - if configured this will be displayed on the login page. This is usually used to have a welcome type message for users before login.
  • Platform Consent Message (optional) - if configured this will be displayed on the login page. This is usually used to display some type of consent message for users to agree to before login. If enabled, a user must check the checkbox displayed to allow login.
  • Platform Consent Confirm Text (optional) - This is displayed next to the platform consent checkbox, if Platform Consent Message is configured. Users must agree to the checkbox before the login prompt will be displayed. This message can be configured, but by default reads: I have read and comply with the above statement
"},{"location":"administration/introduction/#dark-theme-color-scheme","title":"Dark Theme Color Scheme","text":"

Various aspects of the Dark Theme can be dynamically configured in this section.

"},{"location":"administration/introduction/#light-theme-color-scheme","title":"Light Theme Color Scheme","text":"

Various aspects of the Light Theme can be dynamically configured in this section.

"},{"location":"administration/introduction/#tools-configuration-display","title":"Tools Configuration Display","text":"

This section will give general status on the various tools and enabled components of the currently configured OpenCTI deployment.

"},{"location":"administration/merging/","title":"Merging","text":""},{"location":"administration/merging/#data-merging","title":"Data merging","text":"

Within the OpenCTI platform, the merge capability is present into the \"Data > Entities\" tab, and is fairly straightforward to use. To execute a merge, select the set of entities to be merged, then click on the Merge icon.

Merging limitation

It is not possible to merge entities of different types, nor is it possible to merge more than 4 entities at a time (it will have to be done in several stages).

Central to the merging process is the selection of a main entity. This primary entity becomes the anchor, retaining crucial attributes such as name and description. Other entities, while losing specific fields like descriptions, are aliased under the primary entity. This strategic decision preserves vital data while eliminating redundancy.

Once the choice has been made, simply validate to run the task in the background. Depending on the number of entity relationships, and the current workload on the platform, the merge may take more or less time. In the case of a healthy platform and around a hundred relationships per entity, merge is almost instantaneous.

"},{"location":"administration/merging/#data-preservation-and-relationship-continuity","title":"Data preservation and relationship continuity","text":"

A common concern when merging entities lies in the potential loss of information. In the context of OpenCTI, this worry is alleviated. Even if the merged entities were initially created by distinct sources, the platform ensures that data is not lost. Upon merging, the platform automatically generates relationships directly on the merged entity. This strategic approach ensures that all connections, regardless of their origin, are anchored to the consolidated entity. Post-merge, OpenCTI treats these once-separate entities as a singular, unified entity. Subsequent information from varied sources is channeled directly into the entity resulting from the merger. This unified entity becomes the focal point for all future relationships, ensuring the continuity of data and relationships without any loss or fragmentation.

"},{"location":"administration/merging/#important-considerations","title":"Important considerations","text":"
  • Irreversible process: It's essential to know that a merge operation is irreversible. Once completed, the merged entities cannot be reverted to their original state. Consequently, careful consideration and validation are crucial before initiating the merge process.
  • Loss of fields in aliased entities: Fields, such as descriptions, in aliased entities - entities that have not been chosen as the main - will be lost during the merge. Ensuring that essential information is captured in the primary entity is crucial to prevent data loss.
"},{"location":"administration/merging/#additional-resources","title":"Additional resources","text":"
  • Usefulness: To understand the benefits of entity merger, refer to the Merge objects page in the User Guide section of the documentation.
  • Deduplication mechanism: the platform is equipped with deduplication processes that automatically merge data at creation (either manually or by importing data from different sources) if it meets certain conditions.
"},{"location":"administration/notifier-samples/","title":"Notifier samples","text":""},{"location":"administration/notifier-samples/#configure-teams-webhook","title":"Configure Teams webhook","text":"

To configure a notifier for Teams, allowing to send notifications via Teams messages, we followed the guidelines outlined in the Microsoft documentation.

"},{"location":"administration/notifier-samples/#template-message-for-live-trigger","title":"Template message for live trigger","text":"

The Teams template message sent through webhook for a live notification is:

{\n        \"type\": \"message\",\n        \"attachments\": [\n            {\n                \"contentType\": \"application/vnd.microsoft.card.thumbnail\",\n                \"content\": {\n                    \"subtitle\": \"Operation : <%=content[0].events[0].operation%>\",\n                    \"text\": \"<%=(new Date(notification.created)).toLocaleString()%>\",\n                    \"title\": \"<%=content[0].events[0].message%>\",\n                    \"buttons\": [\n                        {\n                            \"type\": \"openUrl\",\n                            \"title\": \"See in OpenCTI\",\n                            \"value\": \"https://YOUR_OPENCTI_URL/dashboard/id/<%=content[0].events[0].instance_id%>\"\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n
"},{"location":"administration/notifier-samples/#template-message-for-digest","title":"Template message for digest","text":"

The Teams template message sent through webhook for a digest notification is:

{\n    \"type\": \"message\",\n    \"attachments\": [\n        {\n            \"contentType\": \"application/vnd.microsoft.card.adaptive\",\n            \"content\": {\n                \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n                \"type\": \"AdaptiveCard\",\n                \"version\": \"1.0\",\n                \"body\": [\n                    {\n                        \"type\": \"Container\",\n                        \"items\": [\n                            {\n                                \"type\": \"TextBlock\",\n                                \"text\": \"<%=notification.name%>\",\n                                \"weight\": \"bolder\",\n                                \"size\": \"extraLarge\"\n                            }, {\n                                \"type\": \"TextBlock\",\n                                \"text\": \"<%=(new Date(notification.created)).toLocaleString()%>\",\n                                \"size\": \"medium\"\n                            }\n                        ]\n                    },\n                    <% for(var i=0; i<content.length; i++) { %>\n                    {\n                        \"type\": \"Container\",\n                        \"items\": [<% for(var j=0; j<content[i].events.length; j++) { %>\n                            {\n                                \"type\" : \"TextBlock\",\n                                \"text\" : \"[<%=content[i].events[j].message%>](https://YOUR_OPENCTI_URL/dashboard/id/<%=content[i].events[j].instance_id%>)\"\n                            }<% if(j<(content[i].events.length - 1)) {%>,<% } %>\n                        <% } %>]\n           }<% if(i<(content.length - 1)) {%>,<% } %>\n                    <% } %>\n                ]\n            }\n        }\n    ],\n   \"dataString\": <%-JSON.stringify(notification)%>\n}\n
"},{"location":"administration/notifiers/","title":"Custom notifiers","text":"

Leveraging the platform's built-in connectors, users can create custom notifiers tailored to their unique needs. OpenCTI features three built-in connectors: a webhook connector, a simple mailer connector, and a platform mailer connector. These connectors operate based on registered schemas that describe their interaction methods.

"},{"location":"administration/notifiers/#built-in-notifier-connectors","title":"Built-In notifier connectors","text":""},{"location":"administration/notifiers/#platform-mailer","title":"Platform mailer","text":"

This notifier connector enables customization of notifications raised within the platform. It's simple to configure, requiring only:

  • Title: The title for the platform notification.
  • Template: Specifies the message template for the notification.
"},{"location":"administration/notifiers/#simple-mailer","title":"Simple mailer","text":"

This notifier connector offers a straightforward approach to email notifications with simplified configuration options. Users can set:

  • Title: The title for the email notification.
  • Header: Additional content to include at the beginning of the email.
  • Footer: Additional content to include at the end of the email.
  • Logo: The option to add a logo to the email.
  • Background Color: Customize the background color of the email.

"},{"location":"administration/notifiers/#webhook","title":"Webhook","text":"

This notifier connector enables users to send notifications to external applications or services through HTTP requests. Users can specify:

  • Verb: Specifies the HTTP method (GET, POST, PUT, DELETE).
  • URL: Defines the destination URL for the webhook.
  • Template: Specifies the message template for the notification.
  • Parameters and Headers: Customizable parameters and headers sent through the webhook request.

OpenCTI provides two notifier samples by default, designed to communicate with Microsoft Teams through a webhook. A documentation page providing details on these samples is available.

"},{"location":"administration/notifiers/#configuration-and-access","title":"Configuration and access","text":"

Custom notifiers are manageable in the \"Settings > Customization > Notifiers\" window and can be restricted through Role-Based Access Control (RBAC). Administrators can control access, limiting usage to specific Users, Groups, or Organizations.

For guidance on configuring notification triggers and exploring the usages of notifiers, refer to the dedicated documentation page.

"},{"location":"administration/ontologies/","title":"Taxonomies","text":"

Taxonomies in OpenCTI refer to the structured classification systems that help in organizing and categorizing cyber threat intelligence data. They play a crucial role in the platform by allowing analysts to systematically tag and retrieve information based on predefined categories and terms.

Along with the Customization page, these pages allow the administrator to customize the platform.

"},{"location":"administration/ontologies/#labels","title":"Labels","text":"

Labels in OpenCTI serve as a powerful tool for organizing, categorizing, and prioritizing data. Here\u2019s how they can be used effectively:

  1. Tagging and Categorization: Labels can be used to tag malware, incidents, or indicators (IOCs) with specific categories, making it easier to filter and search through large datasets.
  2. Prioritization: By labeling threats based on their severity or impact, security analysts can prioritize their response efforts accordingly.
  3. Correlation and Analysis: Labels help in correlating different pieces of intelligence. For example, if multiple indicators are tagged with the same label, it might indicate a larger campaign or a common adversary.
  4. Automation and Integration: Labels can trigger automated workflows (also called playbooks) within OpenCTI. For instance, a label might automatically initiate further investigation or escalate an incident.
  5. Reporting and Metrics: Labels facilitate the generation of reports and metrics, allowing organizations to track trends through dashboards, measure response effectiveness, and make data-driven decisions.
  6. Sharing and Collaboration: When sharing intelligence with other organizations or platforms, labels provide a common language that helps in understanding the context and relevance of the shared data.

Tip

In order to achieve effective data labeling methods, it is recommended to establish a clear and consistent criteria for your labeling and document them in a policy or guideline.

"},{"location":"administration/ontologies/#kill-chain-phases","title":"Kill chain phases","text":"

Kill chain phases are used in OpenCTI to structure and analyze the data related to cyber threats and attacks. They describe the stages of an attack from the perspective of the attacker and provide a framework for identifying, analysing and responding to threats.

OpenCTI supports the following kill chain models:

  • Lockheed Martin Cyber Kill Chain
  • MITRE ATT&CK Framework (Entreprise, PRE, Mobile and ICS)
  • DISARM framework

You can add, edit, or delete kill chain phases in the settings page, and assign them to indicators, attack patterns, incidents, or courses of action in the platform. You can also filter the data by kill chain phase, and view the kill chain phases in a timeline or as a matrix.

"},{"location":"administration/ontologies/#vocabularies","title":"Vocabularies","text":"

Open vocabularies are sets of terms and definitions that are agreed upon by the CTI community. They help to standardize the communication documentation of cyber threat information. This section allows you to customize a set of available fields by adding vocabulary. Almost all of the drop-down menus available in the entities can be modified from this panel.

Open vocabularies in OpenCTI are mainly based on the STIX standard.

"},{"location":"administration/ontologies/#status-templates","title":"Status templates","text":"

Status templates are predefined statuses that can be assigned to different entities in OpenCTI, such as reports, incidents, or cases (incident responses, requests for information and requests for takedown).

They help to track the progress of the analysis and response activities by defining statuses that are used in the workflows.

"},{"location":"administration/ontologies/#case-templates","title":"Case templates","text":"

Customizable case templates help to streamline the process of creating cases with predefined lists of tasks.

"},{"location":"administration/organization-segregation/","title":"Organization segregation","text":"

Enterprise edition

Platform segregation by organization is available under the \"OpenCTI Enterprise Edition\" license. Please read the dedicated page to have all the information.

"},{"location":"administration/organization-segregation/#organization-administration","title":"Organization administration","text":"

Platform administrators can promote members of an organization as \"Organization administrator\". This elevated role grants them the necessary capabilities to create, edit and delete users from the corresponding Organization. Additionally, administrators have the flexibility to define a list of groups that can be granted to newly created members by the organization administrators. This feature simplifies the process of granting appropriate access and privileges to individuals joining the organization.

The platform administrator can promote/demote an organization admin through its user edition form.

Organization admin rights

The \"Organization admin\" has restricted access to Settings. They can only manage the members of the organizations for which they have been promoted as \"admins\".

"},{"location":"administration/parameters/","title":"Parameters","text":""},{"location":"administration/parameters/#description","title":"Description","text":"

This part of the interface wil let you configure global platform settings, like title, favicon, etc.

It will also give you important information about the platform.

"},{"location":"administration/parameters/#the-configuration-section","title":"The \"Configuration\" section","text":"

This section allows the administrator to edit the following settings:

  • Platform title
  • Platform favicon URL
  • Sender email address: email address displayed as sender when sending notifications. The technical sender is defined in the SMTP configuration.
  • Theme
  • Language
  • Hidden entity types: allows you to customize which types of entities you want to see or hide in the platform. This can help you focus on the relevant information and avoid cluttering the platform with unnecessary data.
"},{"location":"administration/parameters/#opencti-platform","title":"OpenCTI Platform","text":"

This is where the Enterprise edition can be enabled.

This section gives important information about the platform like the used version, the edition, the architecture mode (can be Standalone or Cluster) and the number used nodes.

Through the \"Remove Filigran logos\" toggle, the administrator has the option to hide the Filigran logo on the login page and the sidebar.

"},{"location":"administration/parameters/#platform-announcement","title":"Platform Announcement","text":"

This section gives you the possibility to set and display Announcements in the platform. Those announcements will be visible to every user in the platform, on top of the interface.

They can be used to inform some of your users or all of important information, like a scheduled downtime, an incoming upgrade, or even to share important tips regarding the usage of the platform.

An Announcement can be accompanied by a \"Dismiss\u201d button. When clicked by a user, it makes the message disappear for this user.

This option can be deactivated to have a permanent announcement.

\u26a0\ufe0f Only one announcement is shown at a time, with priority given to dismissible ones. If there are no dismissible announcements, the most recent non-dismissible one is shown.

"},{"location":"administration/parameters/#third-party-analytics","title":"Third-party Analytics","text":"

Enterprise edition

Analytics is available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have more information

This is where you can configure analytics providers. At the moment only Google Analytics v4 is supported.

"},{"location":"administration/parameters/#theme-customization","title":"Theme customization","text":"

In this section, the administrator can customize the two OpenCTI themes

"},{"location":"administration/parameters/#tools","title":"Tools","text":"

This section informs the administrator of the statuses of the different managers used in the Platform. More information about the managers can be found here. It shows also the used versions of the search engine database, RabbitMQ and Redis.

In cluster mode, the fact that a manager appears as enabled means that it is active in at least one node.

"},{"location":"administration/policies/","title":"Policies","text":"

The Policies configuration window (in \"Settings > Security > Policies\") encompasses essential settings that govern the organizational sharing, authentication strategies, password policies, login messages, and banner appearance within the OpenCTI platform.

"},{"location":"administration/policies/#platform-main-organization","title":"Platform main organization","text":"

Allow to set a main organization for the entire platform. Users belonging to the main organization enjoy unrestricted access to all data stored in the platform. In contrast, users affiliated with other organizations will only have visibility into data explicitly shared with them.

Numerous repercussions linked to the activation of this feature

This feature has implications for the entire platform and must be fully understood before being used. For example, it's mandatory to have organizations set up for each user, otherwise they won't be able to log in. It is also advisable to include connector's users in the platform main organization to avoid import problems.

"},{"location":"administration/policies/#authentication-strategies","title":"Authentication strategies","text":"

The authentication strategies section provides insights into the configured authentication methods. Additionally, an \"Enforce Two-Factor Authentication\" button is available, allowing administrators to mandate 2FA activation for users, enhancing overall account security.

Please see the Authentication section for further details on available authentication strategies.

"},{"location":"administration/policies/#local-password-policies","title":"Local password policies","text":"

This section encompasses a comprehensive set of parameters defining the local password policy. Administrators can specify requirements such as minimum/maximum number of characters, symbols, digits, and more to ensure robust password security across the platform. Here are all the parameters available:

Parameter Description Number of chars must be greater than or equals to Define the minimum length required for passwords. Number of chars must be lower or equals to (0 equals no maximum) Set an upper limit for password length. Number of symbols must be greater or equals to Specify the minimum number of symbols required in a password. Number of digits must be greater or equals to Set the minimum number of numeric characters in a password. Number of words (split on hyphen, space) must be greater or equals to Enforce a minimum count of words in a password. Number of lowercase chars must be greater or equals to Specify the minimum number of lowercase characters. Number of uppercase chars must be greater or equals to Specify the minimum number of uppercase characters.

"},{"location":"administration/policies/#login-messages","title":"Login messages","text":"

Allow to define messages on the login page to customize and highlight your platform's security policy. Three distinct messages can be customized:

  • Platform login message: Appears above the login form to convey important information or announcements.
  • Platform consent message: A consent message that obscures the login form until users check the approval box, ensuring informed user consent.
  • Platform consent confirm text: A message accompanying the consent box, providing clarity on the consent confirmation process.

"},{"location":"administration/policies/#platform-banner-configuration","title":"Platform banner configuration","text":"

The platform banner configuration section allows administrators to display a custom banner message at the top and bottom of the screen. This feature enables customization for enhanced visual communication and branding within the OpenCTI platform. It can be used to add a disclaimer or system purpose.

This configuration has two parameters:

  • Platform banner level: Options defining the banner background color (Green, Red, or Yellow).
  • Platform banner text: Field referencing the message to be displayed within the banner.

"},{"location":"administration/reasoning/","title":"Rules engine","text":""},{"location":"administration/reasoning/#inference-rules","title":"Inference rules","text":"

The rules engine comprises a set of predefined rules (named inference rules) that govern how new relationships are inferred based on existing data. These rules are carefully crafted to ensure logical and accurate relationship creation. Here is the list of existing inference rules:

"},{"location":"administration/reasoning/#raise-incident-based-on-sighting","title":"Raise incident based on sighting","text":"Conditions Creations A non-revoked Indicator is sighted in an Entity Creation of an Incident linked to the sighted Indicator and the targeted Entity"},{"location":"administration/reasoning/#sightings-of-observables-via-observed-data","title":"Sightings of observables via observed data","text":"Conditions Creations An Indicator is based on an Observable contained in an Observed Data Creation of a sighting between the Indicator and the creating Identity of the Observed Data"},{"location":"administration/reasoning/#sightings-propagation-from-indicator","title":"Sightings propagation from indicator","text":"Conditions Creations An Indicator based on an Observable is sighted in an Entity The Observable is sighted in the Entity"},{"location":"administration/reasoning/#sightings-propagation-from-observable","title":"Sightings propagation from observable","text":"Conditions Creations An Indicator is based on an Observable sighted in an Entity The Indicator is sighted in the Entity"},{"location":"administration/reasoning/#relation-propagation-via-an-observable","title":"Relation propagation via an observable","text":"Conditions Creations An observable is related to two Entities Create a related to relationship between the two Entities"},{"location":"administration/reasoning/#attribution-propagation","title":"Attribution propagation","text":"Conditions Creations An Entity A is attributed to an Entity B and this Entity B is itself attributed to an Entity C The Entity A is attributed to Entity C"},{"location":"administration/reasoning/#belonging-propagation","title":"Belonging propagation","text":"Conditions Creations An Entity A is part of an Entity B and this Entity B is itself part of an Entity C The Entity A is part of Entity C"},{"location":"administration/reasoning/#location-propagation","title":"Location propagation","text":"Conditions Creations A Location A is located at a Location B and this Location B is itself located at a Location C The Location A is located at Location C"},{"location":"administration/reasoning/#organization-propagation-via-participation","title":"Organization propagation via participation","text":"Conditions Creations A User is affiliated with an Organization B, which is part of an Organization C The User is affiliated to the Organization C"},{"location":"administration/reasoning/#identities-propagation-in-reports","title":"Identities propagation in reports","text":"Conditions Creations A Report contains an Identity B and this Identity B is part of an Identity C The Report contains Identity C, as well as the Relationship between Identity B and Identity C"},{"location":"administration/reasoning/#locations-propagation-in-reports","title":"Locations propagation in reports","text":"Conditions Creations A Report contains a Location B and this Location B is located at a Location C The Report contains Location B, as well as the Relationship between Location B and Location C"},{"location":"administration/reasoning/#observables-propagation-in-reports","title":"Observables propagation in reports","text":"Conditions Creations A Report contains an Indicator and this Indicator is based on an Observable The Report contains the Observable, as well as the Relationship between the Indicator and the Observable"},{"location":"administration/reasoning/#usage-propagation-via-attribution","title":"Usage propagation via attribution","text":"Conditions Creations An Entity A, attributed to an Entity C, uses an Entity B The Entity C uses the Entity B"},{"location":"administration/reasoning/#inference-of-targeting-via-a-sighting","title":"Inference of targeting via a sighting","text":"Conditions Creations An Indicator, sighted at an Entity C, indicates an Entity B The Entity B targets the Entity C"},{"location":"administration/reasoning/#targeting-propagation-via-attribution","title":"Targeting propagation via attribution","text":"Conditions Creations An Entity A, attributed to an Entity C, targets an Entity B The Entity C targets the Entity B"},{"location":"administration/reasoning/#targeting-propagation-via-belonging","title":"Targeting propagation via belonging","text":"Conditions Creations An Entity A targets an Identity B, part of an Identity C The Entity A targets the Identity C"},{"location":"administration/reasoning/#targeting-propagation-via-location","title":"Targeting propagation via location","text":"Conditions Creations An Entity targets a Location B and this Location B is located at a Location C The Entity targets the Location C"},{"location":"administration/reasoning/#targeting-propagation-when-located","title":"Targeting propagation when located","text":"Conditions Creations An Entity A targets an Entity B and this target is located at Location D. The Entity A targets the Location D"},{"location":"administration/reasoning/#rule-execution","title":"Rule execution","text":""},{"location":"administration/reasoning/#rule-activation","title":"Rule activation","text":"

When a rule is activated, a background task is initiated. This task scans all platform data, identifying existing relationships that meet the conditions defined by the rule. Subsequently, it creates new objects (entities and/or relationships), expanding the network of insights within your threat intelligence environment. Then, activated rules operate continuously. Whenever a relationship is created or modified, and this change aligns with the conditions specified in an active rule, the reasoning mechanism is triggered. This ensures real-time relationship inference.

"},{"location":"administration/reasoning/#rule-deactivation","title":"Rule deactivation","text":"

Deactivating a rule leads to the deletion of all objects and relationships created by it. This cleanup process maintains the accuracy and reliability of your threat intelligence database.

"},{"location":"administration/reasoning/#access-restrictions-and-data-impact","title":"Access restrictions and data impact","text":"

Access to the rule engine panel is restricted to administrators only. Regular users do not have visibility into this section of the platform. Administrators possess the authority to activate or deactivate rules.

The rules engine empowers OpenCTI with the capability to automatically establish intricate relationships within your data. However, these rules can lead to a very large number of objects created. Even if the operation is reversible, an administrator should consider the impact of activating a rule.

"},{"location":"administration/reasoning/#additional-resources","title":"Additional resources","text":"
  • Usefulness: To understand the benefits and results of these rules, refer to the Inferences and reasoning page in the User Guide section of the documentation.
  • New inference rule: Given the potential impact of a rule on your data, users are not allowed to add new rules. However, users can submit rule suggestions via a GitHub issue for evaluation. These suggestions are carefully evaluated by our team, ensuring continuous improvement and innovation.
"},{"location":"administration/retentions/","title":"Retention policies","text":"

Retention rules serve the purpose of establishing data retention times, specifying when data should be automatically deleted from the platform. Users can define filters to target specific objects. Any object meeting these criteria that haven't been updated within the designated time frame will be permanently deleted.

Note that the data deleted by an active retention policy will not appear in the trash and thus cannot be restored.

"},{"location":"administration/retentions/#configuration","title":"Configuration","text":"

Retention rules can be configured in the \"Settings > Customization > Retention policies\" window. A set of parameters must be configured:

  • Maximum retention days: Set the maximum number of days an object can remain unchanged before being eligible for deletion.
  • Filters: Define filters based on specific criteria to select the types of objects subject to retention rules.

An object will be removed if it meets the specified filters and hasn't been updated for the duration set in the \"Maximum retention days\" field.

"},{"location":"administration/retentions/#verification-process","title":"Verification process","text":"

Before activating a retention rule, users have the option to verify its impact using the \"Verify\" button. This action provides insight into the number of objects that currently match the rule's criteria and would be deleted if the rule is activated.

Verify before activation

Always use the \"Verify\" feature to assess the potential impact of a retention rule before activating it. Once the rule is activated, data deletion will begin, and retrieval of the deleted data will not be possible.

Retention rules contribute to maintaining a streamlined and efficient data lifecycle within OpenCTI, ensuring that outdated or irrelevant information is systematically removed from the platform, thereby optimizing disk space usage.

"},{"location":"administration/segregation/","title":"Marking restriction","text":"

Data segregation in the context of Cyber Threat Intelligence refers to the practice of categorizing and separating different types of data or information related to cybersecurity threats based on specific criteria.

This separation helps organizations manage and analyze threat intelligence more effectively and securely and the goal of data segregation is to ensure that only those individuals who are authorized to view a particular set of data have access to that set of data.

Practically, \"Need-to-know basis\" and \"classification level\" are data segregation measures.

"},{"location":"administration/segregation/#description","title":"Description","text":"

Marking definitions are essential in the context of data segregation to ensure that data is appropriately categorized and protected based on its sensitivity or classification level. Marking definitions establish a standardized framework for classifying data.

Marking Definition objects are unique among STIX objects in the STIX 2.1 standard in that they cannot be versioned. This restriction is in place to prevent the possibility of indirect alterations to the markings associated with a STIX Object.

Multiple markings can be added to the same object. Certain categories of marking definitions or trust groups may enforce rules that specify which markings take precedence over others or how some markings can be added to complement existing ones.

In OpenCTI, data is segregated based on knowledge marking. The diagram provided below illustrates the manner in which OpenCTI establishes connections between pieces of information to authorize data access for a user:

"},{"location":"administration/segregation/#manage-markings","title":"Manage markings","text":""},{"location":"administration/segregation/#create-new-markings","title":"Create new markings","text":"

To create a marking, you must first possess the capability Manage marking definitions. For further information on user administration, please refer to the Users and Role Based Access Control page.

Once you have access to the settings, navigate to \"Settings > Security > Marking Definitions\" to create a new marking.

A marking consists of the following attributes:

  • Type: Specifies the marking group to which it belongs.
  • Definition: The name assigned to the marking.
  • Color: The color associated with the marking.
  • Order: Determines the hierarchical order among markings of the same type.

"},{"location":"administration/segregation/#allowed-marking","title":"Allowed marking","text":"

The configuration of authorized markings for a user is determined at the Group level. To access entities and relationships associated with specific markings, the user must belong to a group that has been granted access to those markings.

There are two ways in which markings can be accessed:

  • The user is a member of a group that has been granted access to the marking.
  • The user is a member of a group that has access to a marking of the same type, with an equal or higher hierarchical order.

Access to an object with several markings

Access to all markings attached to an object is required in order to access it (not only one).

Automatically grant access to the new marking

To allow a group to automatically access a newly created marking definition, you can check Automatically authorize this group to new marking definition.

"},{"location":"administration/segregation/#default-marking-definitions","title":"Default marking definitions","text":"

To apply a default marking when creating a new entity or relationship, you can choose which marking to add by default from the list of allowed markings. You can add only one marking per type, but you can have multiple types. This configuration is also done at the Group level.

Need a configuration change

Simply adding markings as default markings is insufficient to display the markings when creating an entity or relationship. You also need to enable default markings in the customization settings of an entity or relationship. For example, to enable default markings for a new report, navigate to \"Settings > Customization > Report > Markings\" and toggle the option to Activate/Desactivate default values.

"},{"location":"administration/segregation/#maximum-shareable-marking-definitions","title":"Maximum shareable marking definitions","text":"

This configuration allows to define, for each type of marking definitions, until which level we allow to share data externally (via Public dashboard or file export).

The marking definitions that can be shared by a group are the ones

  • that are allowed for this group
  • and whose order are inferior or equal to the order of the maximum shareable markings defined for each marking type.

Users with the Bypass capability can share all the markings.

By default, every marking of a given marking type is shareable.

For example in the capture below, for the type of marking TLP, only data with a marking definition that is allowed and has a level equal or below GREEN will be shareable. And no data with marking definition statement will be shared at all.

"},{"location":"administration/segregation/#management-of-multiple-markings","title":"Management of multiple markings","text":"

In scenarios where multiple markings of the same type but different orders are added, the platform will retain only the marking with the highest order for each type. This consolidation can occurs in various instances:

  • During entity creation, if multiple markings are selected.
  • During entity updates, whether manually or via a connector, if additional markings are introduced.
  • When multiple entities are merged, their respective markings will be amalgamated.

For example:

Create a new report and add markings PAP:AMBER,PAP:RED,TLP:AMBER+STRICT,TLP:CLEAR and a statement CC-BY-SA-4.0 DISARM Foundation

The final markings kept are: PAP:RED, TLP:AMBER+STRICT and CC-BY-SA-4.0 DISARM Foundation

"},{"location":"administration/segregation/#update-an-object-manually","title":"Update an object manually","text":"

When update an entity or a relationship:

  • add a marking with the same type and different orders, a pop-up will be displayed to confirm the choice,
  • add a marking with the same type and the same order, the marking will be added,
  • add a marking with different types, the marking will be added.

"},{"location":"administration/segregation/#import-data-from-a-connector","title":"Import data from a connector","text":"

As a result of this mechanism, when importing data from a connector, the connector is unable to downgrade a marking for an entity if a marking of the same type is already present on it.

"},{"location":"administration/segregation/#additional-information","title":"Additional information","text":"

The Traffic Light Protocol is implemented by default as marking definitions in OpenCTI. It allows you to segregate information by TLP levels in your platform and restrict access to marked data if users are not authorized to see the corresponding marking.

The Traffic Light Protocol (TLP) was designed by the Forum of Incident Response and Security Teams (FIRST) to provide a standardized method for classifying and handling sensitive information, based on four categories of sensitivity.

For more details, the diagram provided below illustrates how are categorized the marking definitions:

"},{"location":"administration/support-package/","title":"Support Package","text":"

Support packages are useful for troubleshooting issue that occurs on OpenCTI platform. Administrators can request to create and download a support package that contains recent platform error logs and usage statistics.

Support package content

Even if we do our best to prevent logging any data, the support package may contains some sensitive information that you may not want to share with everyone. Before creating a ticket with your support package takes some time to check if you can safely share the content depending of your security policy.

Support Package can be requested from \"Settings > Support\" menu.

"},{"location":"administration/support-package/#package-generation","title":"Package generation","text":"

On a click on \"Generate support package\", a support event is propagated to every platform instances to request needed information. Every instance that will receive this message will process the request and send the files to the platform. During this processing the interface will display the expected support package name in an IN PROGRESS state waiting for completion. After finishing the process the support package will move to the READY state and the buttons download and delete will be activated.

"},{"location":"administration/support-package/#package-download","title":"Package download","text":"

After file generation, using the download button will dynamically create a (zip) containing all instances logs and telemetry.

"},{"location":"administration/support-package/#partial-package","title":"Partial package","text":"

In case of platform instability, some logs might not be retrieved and the support package will be incomplete.

If some instances fail to send their data, you will be able to force download a partial zip only after 1 minute. In case of a support package taking more than 5 minutes, the status will be moved to \"timeout\".

"},{"location":"administration/users/","title":"Users and Role Based Access Control","text":""},{"location":"administration/users/#introduction","title":"Introduction","text":"

In OpenCTI, the RBAC system not only related to what users can do or cannot do in the platform (aka. Capabilities) but also to the system of data segregation. Also, platform behavior such as default home dashboards, default triggers and digests as well as default hidden menus or entities can be defined across groups and organizations.

"},{"location":"administration/users/#high-level-design","title":"High level design","text":""},{"location":"administration/users/#roles","title":"Roles","text":"

Roles are used in the platform to grant the given groups with some capabilities to define what users in those groups can do or cannot do.

"},{"location":"administration/users/#list-of-capabilities","title":"List of capabilities","text":"Capability Description Bypass all capabilities Just bypass everything including data segregation and enforcements. Access knowledge Access in read-only to all the knowledge in the platform. Access to collaborative creation Create notes and opinions (and modify its own) on entities and relations. Create / Update knowledge Create and update existing entities and relationships. Restrict organization access Share entities and relationships with other organizations. Delete knowledge Delete entities and relationships. Upload knowledge files Upload files in the Data and Content section of entities. Download knowledge export Download the exports generated in the entities (in the Data section). Ask for knowledge enrichment Trigger an enrichment for a given entity. Access Dashboards and investigations Access to existing custom dashboards and investigations. Create / Update Dashboards and investigations Create and update custom dashboards and investigations. Delete Dashboards and investigations Delete existing custom dashboards and investigations. Manage Public Dashboards Manage public dashboards Access connectors Read information in the Data > Connectors section. Manage connector state Reset the connector state to restart ingestion from the beginning. Access data sharing & ingestion Access and consume data such as TAXII collections. Manage data sharing & ingestion Share data such as TAXII collections or custom dashboards. Manage CSV mappers Create, update and delete CSV mappers. Access administration Access and manage overall parameters of the platform in Settings > Parameters. Manage credentials Access and manage roles, groups, users, organizations and security policies. Manage marking definitions Update and delete marking definitions. Manage labels & Attributes Update and delete labels, custom taxonomies, workflow and case templates. Connectors API usage: register, ping, export push ... Connectors specific permissions for register, ping, push export files, etc. Connect and consume the platform streams (/stream, /stream/live) List and consume the OpenCTI live streams. Bypass mandatory references if any If external references enforced in a type of entity, be able to bypass the enforcement."},{"location":"administration/users/#manage-roles","title":"Manage roles","text":"

You can manage the roles in Settings > Security > Roles.

To create a role, just click on the + button:

Then you will be able to define the capabilities of the role:

"},{"location":"administration/users/#users","title":"Users","text":"

You can manage the users in Settings > Security > Users. If you are using Single-Sign-On (SSO), the users in OpenCTI are automatically created upon login.

To create a user, just click on the + button:

"},{"location":"administration/users/#manage-a-user","title":"Manage a user","text":"

When access to a user, it is possible to:

  • Visualize information including the token
  • Modify it, reset 2FA if necessary
  • Manage its sessions
  • Manage its triggers and digests
  • Visualize the history and operations
  • Manage its max confidence levels

From this view you can edit the user's information by clicking the \"Update\" button, which opens a panel with several tabs.

  • Overview tab: edit all basic information such as the name or language
  • Password tab: change the password for this user
  • Groups tab: select the groups this user belongs to
  • Organization Admin tab: see Organization administration
  • Confidences tab: manage the user's maximum confidence level and overrides per entity type

Mandatory max confidence level

A user without Max confidence level won't have the ability to create, delete or update any data in our platform. Please be sure that your users are always either assigned to group that have a confidence level defined or that have an override of this group confidence level.

"},{"location":"administration/users/#groups","title":"Groups","text":"

Groups are the main way to manage permissions and data segregation as well as platform customization for the given users part of this group. You can manage the groups in Settings > Security > Groups.

Here is the description of the group available parameters.

Parameter Description Auto new markings If a new marking definition is created, this group will automatically be granted to it. Default membership If a new user is created (manually or upon SSO), it will be added to this group. Roles Roles and capabilities granted to the users belonging to this group. Default dashboard Customize the home dashboard for the users belonging to this group. Default markings In Settings > Customization > Entity types, if a default marking definition is enabled, default markings of the group is used. Allowed markings Grant access to the group to the defined marking definitions, more details in data segregation. Max shareable markings Grant authorization to the group to share marking definitions. Triggers and digests Define defaults triggers and digests for the users belonging to this group. Max confidence level Define the maximum confidence level for the group: it will impact the capacity to update entities, the confidence level of a newly created entity by a user of the group.

Max confidence level when a user has multiple groups

A user with multiple groups will have the the highest confidence level of all its groups. For instance, if a user is part of group A (max confidence level = 100) and group B (max confidence level = 50), then the user max confidence level will be 100.

"},{"location":"administration/users/#manage-a-group","title":"Manage a group","text":"

When managing a group, you can define the members and all above configurations.

"},{"location":"administration/users/#organizations","title":"Organizations","text":"

Users can belong to organizations, which is an additional layer of data segregation and customization. To find out more about this part, please refer to the page on organization segregation.

"},{"location":"administration/users/#organization-administration","title":"Organization administration","text":"

Platform administrators can promote members of an organization as \"Organization administrator\". This elevated role grants them the necessary capabilities to create, edit and delete users from the corresponding Organization. Additionally, administrators have the flexibility to define a list of groups that can be granted to newly created members by the organization administrators. This feature simplifies the process of granting appropriate access and privileges to individuals joining the organization.

The platform administrator can promote/demote an organization admin through its user edition form.

Organization admin rights

The \"Organization admin\" has restricted access to Settings. They can only manage the members of the organizations for which they have been promoted as \"admins\".

"},{"location":"administration/audit/configuration/","title":"Configuration","text":"

Enterprise edition

Activity unified interface and logging are available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all information

As explained in the overview page, all administration actions are listened by default. However, all knowledge are not listened by default due to performance impact on the platform.

For this reason you need to explicitly activate extended listening on user / group or organization.

Listening will start just after the configuration. Every past events will not be taken into account.

"},{"location":"administration/audit/events/","title":"Events","text":"

Enterprise edition

Activity unified interface and logging are available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all information

"},{"location":"administration/audit/events/#description","title":"Description","text":"

OpenCTI activity capability is the way to unified whats really happen in the platform. In events section you will have access to the UI that will answer to \"who did what, where, and when?\" within your data with the maximum level of transparency.

"},{"location":"administration/audit/events/#include-knowledge","title":"Include knowledge","text":"

By default, the events screen only show you the administration actions done by the users.

If you want to see also the information about the knowledge, you can simply activate the filter in the bar to get the complete overview of all user actions.

Don't hesitate to read again the overview page to have a better understanding of the difference between Audit, Basic/Extended knowledge.

"},{"location":"administration/audit/overview/","title":"Overview","text":""},{"location":"administration/audit/overview/#overview","title":"Overview","text":"

Enterprise edition

Activity unified interface and logging are available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all the information

OpenCTI activity capability is the way to unify what's really happening in the platform. With this feature you will be able to answer \"who did what, where, and when?\" within your data with the maximum level of transparency.

Enabling activity helps your security, auditing, and compliance entities monitor platform for possible vulnerabilities or external data misuse.

"},{"location":"administration/audit/overview/#categories","title":"Categories","text":"

The activity groups 3 different concepts that need to be explained.

"},{"location":"administration/audit/overview/#basic-knowledge","title":"Basic knowledge","text":"

The basic knowledge refers to all STIX data knowledge inside OpenCTI. Every create/update/delete action on that knowledge is accessible through the history. That basic activity is handled by the history manager and can also be found directly on each entity.

"},{"location":"administration/audit/overview/#extended-knowledge","title":"Extended knowledge","text":"

The extended knowledge refers to extra information data to track specific user activity. As this kind of tracking is expensive, the tracking will only be done for specific users/groups/organizations explicitly configured in the configuration window.

"},{"location":"administration/audit/overview/#audit-knowledge","title":"Audit knowledge","text":"

Audit is focusing on user administration or security actions. Audit will produce console/logs files along with user interface elements.

{\n  \"auth\": \"<User information>\",\n  \"category\": \"AUDIT\",\n  \"level\": \"<info | error>\",\n  \"message\": \"<human readable explanation>\",\n  \"resource\": {\n    \"type\": \"<authentication | mutation>\",\n    \"event_scope\": \"<depends on type>\",\n    \"event_access\": \"<administration>\",\n    \"data\": \"<contextual data linked to the event type>\",\n    \"version\": \"<version of audit log format>\"\n  },\n  \"timestamp\": \"<event date>\",\n  \"version\": \"<platform version>\"\n}\n
"},{"location":"administration/audit/overview/#architecture","title":"Architecture","text":"

OpenCTI uses different mechanisms to be able to publish actions (audit) or data modification (history)

"},{"location":"administration/audit/overview/#audit-knowledge_1","title":"Audit knowledge","text":"

Administration or security actions

With Enterprise edition activated, Administration and security actions are always written; you can't configure, exclude, or disable them

Supported

Not supported for now

Not applicable

"},{"location":"administration/audit/overview/#ingestion","title":"Ingestion","text":"Create Delete Edit Remote OCTI Streams"},{"location":"administration/audit/overview/#data-sharing","title":"Data sharing","text":"Create Delete Edit CSV Feeds TAXII Feeds Stream Feeds"},{"location":"administration/audit/overview/#connectors","title":"Connectors","text":"Create Delete Edit Connectors State reset Works"},{"location":"administration/audit/overview/#parameters","title":"Parameters","text":"Create Delete Edit Platform parameters"},{"location":"administration/audit/overview/#security","title":"Security","text":"Create Delete Edit Roles Groups Users Sessions Policies"},{"location":"administration/audit/overview/#customization","title":"Customization","text":"Create Delete Edit Entity types Rules engine Retention policies"},{"location":"administration/audit/overview/#taxonomies","title":"Taxonomies","text":"Create Delete Edit Status templates Case templates + tasks"},{"location":"administration/audit/overview/#accesses","title":"Accesses","text":"Listen Login (success or fail) Logout Unauthorized access"},{"location":"administration/audit/overview/#extended-knowledge_1","title":"Extended knowledge","text":"

Extended knowledge

Extented knowledge activity are written only if you activate the feature for a subset of users / groups or organizations

"},{"location":"administration/audit/overview/#data-management","title":"Data management","text":"

Some history actions are already included in the \"basic knowledge\". (basic marker)

Read Create Delete Edit Platform knowledge basic basic basic Background tasks knowledge Knowledge files basic basic Global data import files Analyst workbenches files Triggers Workspaces Investigations User profile"},{"location":"administration/audit/overview/#user-actions","title":"User actions","text":"Supported Ask for file import Ask for data enrichment Ask for export generation Execute global search"},{"location":"administration/audit/triggers/","title":"Activity triggers","text":"

Enterprise edition

Activity unified interface and logging are available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all information

"},{"location":"administration/audit/triggers/#description","title":"Description","text":"

Having all the history in the user interface (events) is sometimes not enough to have a proactive monitoring. For this reason, you can configure some specific triggers to receive notifications on audit events. You can configure like personal triggers, lives one that will be sent directly or digest depending on your needs.

"},{"location":"administration/audit/triggers/#configuration","title":"Configuration","text":"

In this type of trigger, you will need to configure various options:

  • Notification target: user interface or email,
  • Recipients: the recipient of the notification,
  • Filters: a set of filters to get only events that really interest you.
"},{"location":"administration/audit/triggers/#event-structure","title":"Event structure","text":"

In order to correctly configure the filters, here's a definition of the event structure

  • Event type: authentication

    • Event scopes: login and logout
  • Event type: read

    • Event scopes: read and unauthorized
  • Event type: file

    • Event scopes: read, create and delete
  • Event type: mutation

    • Event scopes: unauthorized, update, create and delete
  • Event type: command

    • Event scopes: search, enrich, import and export
"},{"location":"deployment/authentication/","title":"Authentication","text":""},{"location":"deployment/authentication/#introduction","title":"Introduction","text":"

OpenCTI supports several authentication providers. If you configure multiple strategies, they will be tested in the order you declared them.

Activation

You need to configure/activate only the authentication strategy that you really want to propose to your users.

The product proposes two kinds of authentication strategies:

  • Form (asking user for a user/password),
  • Buttons (click with authentication on an external system).
"},{"location":"deployment/authentication/#supported-strategies","title":"Supported Strategies","text":"

Under the hood, we technically use the strategies provided by PassportJS. We integrate a subset of the strategies available with passport. If you need more, we can integrate other strategies.

"},{"location":"deployment/authentication/#local-users-form","title":"Local users (form)","text":"

This strategy uses the OpenCTI database as a user management.

OpenCTI use this strategy as the default, but it's not the one we recommend for security reasons.

\"local\": {\n    \"strategy\": \"LocalStrategy\",\n    \"config\": {\n        \"disabled\": false\n    }\n}\n

Production deployment

Please use the LDAP/Auth0/OpenID/SAML strategy for production deployment.

"},{"location":"deployment/authentication/#ldap-form","title":"LDAP (form)","text":"

This strategy can be used to authenticate your user with your company LDAP and is based on Passport - LDAPAuth.

\"ldap\": {\n    \"strategy\": \"LdapStrategy\",\n    \"config\": {\n        \"url\": \"ldaps://mydc.domain.com:686\",\n        \"bind_dn\": \"cn=Administrator,cn=Users,dc=mydomain,dc=com\",\n        \"bind_credentials\": \"MY_STRONG_PASSWORD\",\n        \"search_base\": \"cn=Users,dc=mydomain,dc=com\",\n        \"search_filter\": \"(cn={{username}})\",\n        \"mail_attribute\": \"mail\",\n        // \"account_attribute\": \"givenName\",\n        // \"firstname_attribute\": \"cn\",\n        // \"lastname_attribute\": \"cn\",\n        \"account_attrgroup_search_filteribute\": \"givenName\",\n        \"allow_self_signed\": true\n    }\n}\n

If you would like to use LDAP groups to automatically associate LDAP groups and OpenCTI groups/organizations:

\"ldap\": {\n    \"config\": {\n        ...\n        \"group_search_base\": \"cn=Groups,dc=mydomain,dc=com\",\n        \"group_search_filter\": \"(member={{dn}})\",\n        \"groups_management\": { // To map LDAP Groups to OpenCTI Groups\n            \"group_attribute\": \"cn\",\n            \"groups_mapping\": [\"LDAP_Group_1:OpenCTI_Group_1\", \"LDAP_Group_2:OpenCTI_Group_2\", ...]\n        },\n        \"organizations_management\": { // To map LDAP Groups to OpenCTI Organizations\n            \"organizations_path\": \"cn\",\n            \"organizations_mapping\": [\"LDAP_Group_1:OpenCTI_Organization_1\", \"LDAP_Group_2:OpenCTI_Organization_2\", ...]\n        }\n    }\n}\n
"},{"location":"deployment/authentication/#saml-button","title":"SAML (button)","text":"

This strategy can be used to authenticate your user with your company SAML and is based on Passport - SAML.

\"saml\": {\n    \"identifier\": \"saml\",\n    \"strategy\": \"SamlStrategy\",\n    \"config\": {\n        \"issuer\": \"mytestsaml\",\n        // \"account_attribute\": \"nameID\",\n        // \"firstname_attribute\": \"nameID\",\n        // \"lastname_attribute\": \"nameID\",\n        \"entry_point\": \"https://auth.mydomain.com/auth/realms/mydomain/protocol/saml\",\n        \"saml_callback_url\": \"http://localhost:4000/auth/saml/callback\",\n        // \"private_key\": \"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwg...\",\n        \"cert\": \"MIICmzCCAYMCBgF2Qt3X1zANBgkqhkiG9w0BAQsFADARMQ8w...\",\n        \"logout_remote\": false\n    }\n}\n

For the SAML strategy to work:

  • The cert parameter is mandatory (PEM format) because it is used to validate the SAML response.
  • The private_key (PEM format) is optional and is only required if you want to sign the SAML client request.

Certificates

Be careful to put the cert / private_key key in PEM format. Indeed, a lot of systems generally export the keys in X509 / PCKS12 formats and so you will need to convert them. Here is an example to extract PEM from PCKS12:

openssl pkcs12 -in keystore.p12 -out newfile.pem -nodes\n

Here is an example of SAML configuration using environment variables:

- PROVIDERS__SAML__STRATEGY=SamlStrategy \n- \"PROVIDERS__SAML__CONFIG__LABEL=Login with SAML\"\n- PROVIDERS__SAML__CONFIG__ISSUER=mydomain\n- PROVIDERS__SAML__CONFIG__ENTRY_POINT=https://auth.mydomain.com/auth/realms/mydomain/protocol/saml\n- PROVIDERS__SAML__CONFIG__SAML_CALLBACK_URL=http://opencti.mydomain.com/auth/saml/callback\n- PROVIDERS__SAML__CONFIG__CERT=MIICmzCCAYMCBgF3Rt3X1zANBgkqhkiG9w0BAQsFADARMQ8w\n- PROVIDERS__SAML__CONFIG__LOGOUT_REMOTE=false\n

OpenCTI supports mapping SAML Roles/Groups on OpenCTI Groups. Here is an example:

\"saml\": {\n    \"config\": {\n        ...,\n        // Groups mapping\n        \"groups_management\": { // To map SAML Groups to OpenCTI Groups\n            \"group_attributes\": [\"Group\"],\n            \"groups_mapping\": [\"SAML_Group_1:OpenCTI_Group_1\", \"SAML_Group_2:OpenCTI_Group_2\", ...]\n        },\n        \"groups_management\": { // To map SAML Roles to OpenCTI Groups\n            \"group_attributes\": [\"Role\"],\n            \"groups_mapping\": [\"SAML_Role_1:OpenCTI_Group_1\", \"SAML_Role_2:OpenCTI_Group_2\", ...]\n        },\n        // Organizations mapping\n        \"organizations_management\": { // To map SAML Groups to OpenCTI Organizations\n            \"organizations_path\": [\"Group\"],\n            \"organizations_mapping\": [\"SAML_Group_1:OpenCTI_Organization_1\", \"SAML_Group_2:OpenCTI_Organization_2\", ...]\n        },\n        \"organizations_management\": { // To map SAML Roles to OpenCTI Organizations\n            \"organizations_path\": [\"Role\"],\n            \"organizations_mapping\": [\"SAML_Role_1:OpenCTI_Organization_1\", \"SAML_Role_2:OpenCTI_Organization_2\", ...]\n        }\n    }\n}\n

Here is an example of SAML Groups mapping configuration using environment variables:

- \"PROVIDERS__SAML__CONFIG__GROUPS_MANAGEMENT__GROUP_ATTRIBUTES=[\\\"Group\\\"]\"\n- \"PROVIDERS__SAML__CONFIG__GROUPS_MANAGEMENT__GROUPS_MAPPING=[\\\"SAML_Group_1:OpenCTI_Group_1\\\", \\\"SAML_Group_2:OpenCTI_Group_2\\\", ...]\"\n
"},{"location":"deployment/authentication/#auth0-button","title":"Auth0 (button)","text":"

This strategy allows to use Auth0 Service to handle the authentication and is based on Passport - Auth0.

\"authzero\": {\n    \"identifier\": \"auth0\",\n    \"strategy\": \"Auth0Strategy\",\n    \"config\": {\n        \"clientID\": \"XXXXXXXXXXXXXXXXXX\",\n        \"baseURL\": \"https://opencti.mydomain.com\",\n        \"clientSecret\": \"XXXXXXXXXXXXXXXXXX\",\n        \"callback_url\": \"https://opencti.mydomain.com/auth/auth0/callback\",\n        \"domain\": \"mycompany.eu.auth0.com\",\n        \"audience\": \"XXXXXXXXXXXXXXX\",\n        \"scope\": \"openid email profile XXXXXXXXXXXXXXX\",\n        \"logout_remote\": false\n    }\n}\n

Here is an example of Auth0 configuration using environment variables:

- PROVIDERS__AUTHZERO__STRATEGY=Auth0Strategy\n- PROVIDERS__AUTHZERO__CONFIG__CLIENT_ID=${AUTH0_CLIENT_ID}\n- PROVIDERS__AUTHZERO__CONFIG__BASEURL=${AUTH0_BASE_URL}\n- PROVIDERS__AUTHZERO__CONFIG__CLIENT_SECRET=${AUTH0_CLIENT_SECRET}\n- PROVIDERS__AUTHZERO__CONFIG__CALLBACK_URL=${AUTH0_CALLBACK_URL}\n- PROVIDERS__AUTHZERO__CONFIG__DOMAIN=${AUTH0_DOMAIN}\n- \"PROVIDERS__AUTHZERO__CONFIG__SCOPE=openid email profile\"\n- PROVIDERS__AUTHZERO__CONFIG__LOGOUT_REMOTE=false\n
"},{"location":"deployment/authentication/#openid-connect-button","title":"OpenID Connect (button)","text":"

This strategy allows to use the OpenID Connect Protocol to handle the authentication and is based on Node OpenID Client which is more powerful than the passport one.

\"oic\": {\n    \"identifier\": \"oic\",\n    \"strategy\": \"OpenIDConnectStrategy\",\n    \"config\": {\n        \"label\": \"Login with OpenID\",\n        \"issuer\": \"https://auth.mydomain.com/auth/realms/mydomain\",\n        \"client_id\": \"XXXXXXXXXXXXXXXXXX\",\n        \"client_secret\": \"XXXXXXXXXXXXXXXXXX\",\n        \"redirect_uris\": [\"https://opencti.mydomain.com/auth/oic/callback\"],\n        \"logout_remote\": false\n    }\n}\n

Here is an example of OpenID configuration using environment variables:

- PROVIDERS__OPENID__STRATEGY=OpenIDConnectStrategy \n- \"PROVIDERS__OPENID__CONFIG__LABEL=Login with OpenID\"\n- PROVIDERS__OPENID__CONFIG__ISSUER=https://auth.mydomain.com/auth/realms/xxxx\n- PROVIDERS__OPENID__CONFIG__CLIENT_ID=XXXXXXXXXXXXXXXXXX\n- PROVIDERS__OPENID__CONFIG__CLIENT_SECRET=XXXXXXXXXXXXXXXXXX\n- \"PROVIDERS__OPENID__CONFIG__REDIRECT_URIS=[\\\"https://opencti.mydomain.com/auth/oic/callback\\\"]\"\n- PROVIDERS__OPENID__CONFIG__LOGOUT_REMOTE=false\n

OpenCTI support mapping OpenID Claims on OpenCTI Groups (everything is tied to a group in the platform). Here is an example:

\"oic\": {\n    \"config\": {\n        ...,\n        // Groups mapping\n        \"groups_management\": { // To map OpenID Claims to OpenCTI Groups\n            \"groups_scope\": \"groups\",\n            \"groups_path\": [\"groups\", \"realm_access.groups\", \"resource_access.account.groups\"],\n            \"groups_mapping\": [\"OpenID_Group_1:OpenCTI_Group_1\", \"OpenID_Group_2:OpenCTI_Group_2\", ...]\n        },\n        // Organizations mapping  \n        \"organizations_management\": { // To map OpenID Claims to OpenCTI Organizations\n            \"organizations_scope\": \"groups\",\n            \"organizations_path\": [\"groups\", \"realm_access.groups\", \"resource_access.account.groups\"],\n            \"organizations_mapping\": [\"OpenID_Group_1:OpenCTI_Group_1\", \"OpenID_Group_2:OpenCTI_Group_2\", ...]\n        },\n    }\n}\n

Here is an example of OpenID Groups mapping configuration using environment variables:

- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_SCOPE=groups\n- \"PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_PATH=[\\\"groups\\\", \\\"realm_access.groups\\\", \\\"resource_access.account.groups\\\"]\"\n- \"PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__GROUPS_MAPPING=[\\\"OpenID_Group_1:OpenCTI_Group_1\\\", \\\"OpenID_Group_2:OpenCTI_Group_2\\\", ...]\"\n

By default, the claims are mapped based on the content of the JWT access_token. If you want to map claims which are in other JWT (such as id_token), you can define the following environment variables:

- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__TOKEN_REFERENCE=id_token\n- PROVIDERS__OPENID__CONFIG__ORGANISATIONS_MANAGEMENT__TOKEN_REFERENCE=id_token\n

Alternatively, you can request OpenCTI to use claims from the userinfo endpoint instead of a JWT.

- PROVIDERS__OPENID__CONFIG__GROUPS_MANAGEMENT__READ_USERINFO=true\n- PROVIDERS__OPENID__CONFIG__ORGANISATIONS_MANAGEMENT__READ_USERINFO=true\n

"},{"location":"deployment/authentication/#facebook-button","title":"Facebook (button)","text":"

This strategy can authenticate your users with Facebook and is based on Passport - Facebook.

\"facebook\": {\n    \"identifier\": \"facebook\",\n    \"strategy\": \"FacebookStrategy\",\n    \"config\": {\n        \"client_id\": \"XXXXXXXXXXXXXXXXXX\",\n        \"client_secret\": \"XXXXXXXXXXXXXXXXXX\",\n        \"callback_url\": \"https://opencti.mydomain.com/auth/facebook/callback\",\n        \"logout_remote\": false\n    }\n}\n
"},{"location":"deployment/authentication/#google-button","title":"Google (button)","text":"

This strategy can authenticate your users with Google and is based on Passport - Google.

\"google\": {\n    \"identifier\": \"google\",\n    \"strategy\": \"GoogleStrategy\",\n    \"config\": {\n        \"client_id\": \"XXXXXXXXXXXXXXXXXX\",\n        \"client_secret\": \"XXXXXXXXXXXXXXXXXX\",\n        \"callback_url\": \"https://opencti.mydomain.com/auth/google/callback\",\n        \"logout_remote\": false\n    }\n}\n
"},{"location":"deployment/authentication/#github-button","title":"GitHub (button)","text":"

This strategy can authenticate your users with GitHub and is based on Passport - GitHub.

\"github\": {\n    \"identifier\": \"github\",\n    \"strategy\": \"GithubStrategy\",\n    \"config\": {\n        \"client_id\": \"XXXXXXXXXXXXXXXXXX\",\n        \"client_secret\": \"XXXXXXXXXXXXXXXXXX\",\n        \"callback_url\": \"https://opencti.mydomain.com/auth/github/callback\",\n        \"logout_remote\": false\n  }\n}\n
"},{"location":"deployment/authentication/#client-certificate-button","title":"Client certificate (button)","text":"

This strategy can authenticate a user based on SSL client certificates. For this, you need to configure OpenCTI to start in HTTPS, for example:

\"port\": 443,\n\"https_cert\": {\n    \"key\": \"/cert/server_key.pem\",\n    \"crt\": \"/cert/server_cert.pem\",\n    \"reject_unauthorized\": true\n}\n

And then add the ClientCertStrategy:

\"cert\": {\n    \"strategy\":\"ClientCertStrategy\",\n    \"config\": {\n        \"label\":\"CLIENT CERT\"\n    }\n}\n

Afterwards, when accessing for the first time OpenCTI, the browser will ask for the certificate you want to use.

"},{"location":"deployment/authentication/#proxy-headers-automatic","title":"Proxy headers (automatic)","text":"

This strategy can authenticate the users directly from trusted headers.

{\n  \"header\": {\n    \"strategy\": \"HeaderStrategy\",\n    \"config\": {\n      \"disabled\": false,\n      \"header_email\": \"auth_email_address\",\n      \"header_name\": \"auth_name\",\n      \"header_firstname\": \"auth_firstname\",\n      \"header_lastname\": \"auth_lastname\",\n      \"logout_uri\": \"https://www.filigran.io\",\n      \"groups_management\": {\n        \"groups_header\": \"auth_groups\",\n        \"groups_splitter\": \",\",\n        \"groups_mapping\": [\"admin:admin\", \"root:root\"]\n      },\n      \"organizations_management\": {\n        \"organizations_header\": \"auth_institution\",\n        \"organizations_splitter\": \",\",\n        \"organizations_mapping\": [\"test:test\"]\n      }\n    }\n  }\n}\n

If this mode is activated and the headers are available, the user will be automatically logged without any action or notice. The logout uri will remove the session and redirect to the configured uri. If not specified, the redirect will be done to the request referer and so the header authentication will be done again.

"},{"location":"deployment/authentication/#automatically-create-group-on-sso","title":"Automatically create group on SSO","text":"

The variable auto_create_group can be added in the options of some strategies (LDAP, SAML and OpenID). If this variable is true, the groups of a user that logins will automatically be created if they don\u2019t exist.

More precisely, if the user that tries to authenticate has groups that don\u2019t exist in OpenCTI but exist in the SSO configuration, there are two cases:

  • if auto_create_group= true in the SSO configuration: the groups are created at the platform initialization and the user will be mapped on them.
  • else: an error is raised.

Example

We assume that Group1 exists in the platform, and newGroup doesn\u2019t exist. The user that tries to log in has the group newGroup. If auto_create_group = true in the SSO configuration, the group named newGroup will be created at the platform initialization and the user will be mapped on it. If auto_create_group = false or is undefined, the user can\u2019t log in and an error is raised.

\"groups_management\": {\n  \"group_attribute\": \"cn\",\n  \"groups_mapping\": [\"SSO_GROUP_NAME1:group1\", \"SSO_GROUP_NAME_2:newGroup\", ...]\n},\n\"auto_create_group\": true\n
"},{"location":"deployment/authentication/#examples","title":"Examples","text":""},{"location":"deployment/authentication/#ldap-then-fallback-to-local","title":"LDAP then fallback to local","text":"

In this example the users have a login form and need to enter login and password. The authentication is done on LDAP first, then locally if user failed to authenticate and finally fail if none of them succeeded. Here is an example for the production.json file:

\"providers\": {\n    \"ldap\": {\n        \"strategy\": \"LdapStrategy\",\n        \"config\": {\n            \"url\": \"ldaps://mydc.mydomain.com:636\",\n            \"bind_dn\": \"cn=Administrator,cn=Users,dc=mydomain,dc=com\",\n            \"bind_credentials\": \"MY_STRONG_PASSWORD\",\n            \"search_base\": \"cn=Users,dc=mydomain,dc=com\",\n            \"search_filter\": \"(cn={{username}})\",\n            \"mail_attribute\": \"mail\",\n            \"account_attribute\": \"givenName\"\n        }\n    },\n    \"local\": {\n        \"strategy\": \"LocalStrategy\",\n        \"config\": {\n            \"disabled\": false\n        }\n    }\n}\n

If you use a container deployment, here is an example using environment variables:

- PROVIDERS__LDAP__STRATEGY=LdapStrategy\n- PROVIDERS__LDAP__CONFIG__URL=ldaps://mydc.mydomain.org:636\n- PROVIDERS__LDAP__CONFIG__BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=com\n- PROVIDERS__LDAP__CONFIG__BIND_CREDENTIALS=XXXXXXXXXX\n- PROVIDERS__LDAP__CONFIG__SEARCH_BASE=cn=Users,dc=mydomain,dc=com\n- PROVIDERS__LDAP__CONFIG__SEARCH_FILTER=(cn={{username}})\n- PROVIDERS__LDAP__CONFIG__MAIL_ATTRIBUTE=mail\n- PROVIDERS__LDAP__CONFIG__ACCOUNT_ATTRIBUTE=givenName\n- PROVIDERS__LDAP__CONFIG__ALLOW_SELF_SIGNED=true\n- PROVIDERS__LOCAL__STRATEGY=LocalStrategy\n
"},{"location":"deployment/clustering/","title":"Clustering","text":""},{"location":"deployment/clustering/#introduction","title":"Introduction","text":"

The OpenCTI platform technological stack has been designed to be able to scale horizontally. All dependencies such as Elastic or Redis can be deployed in cluster mode and performances can be drastically increased by deploying multiple platform and worker instances.

"},{"location":"deployment/clustering/#high-level-architecture","title":"High level architecture","text":"

Here is the high level architecture for customers and Filigran cloud platform to ensure both high availability and throughput.

"},{"location":"deployment/clustering/#configuration","title":"Configuration","text":""},{"location":"deployment/clustering/#dependencies","title":"Dependencies","text":""},{"location":"deployment/clustering/#elasticsearch","title":"ElasticSearch","text":"

In the ElasticSearch configuration of OpenCTI, it is possible to declare all nodes.

- \"ELASTICSEARCH__URL=[\\\"https://user:pass@node1:9200\\\", \\\"https://user:pass@node2:9200\\\", ...]\"\n

Compatibility

OpenCTI is also compatible with OpenSearch and AWS / GCP / Azure native search services based on the ElasticSearch query language.

"},{"location":"deployment/clustering/#redis","title":"Redis","text":"

Redis should be turned to cluster mode:

- REDIS__MODE=cluster\n- \"REDIS__HOSTNAMES=[\\\"node1:6379\\\", \\\"node2:6379\\\", ...]\"\n

Compatibility

OpenCTI is also compatible with ElastiCache, MemoryStore and AWS / GCP / Azure native services based on the Redis protocol.

"},{"location":"deployment/clustering/#rabbitmq","title":"RabbitMQ","text":"

For the RabbitMQ cluster, you will need a TCP load balancer on top of the nodes since the configuration does not support multi-nodes for now:

- RABBITMQ__HOSTNAME=load-balancer-rabbitmq\n

Compatibility

OpenCTI is also compatible with Amazon MQ, CloudAMQP and AWS / GCP / Azure native services based on the AMQP protocol.

"},{"location":"deployment/clustering/#s3-bucket-minio","title":"S3 bucket / MinIO","text":"

MinIO is an open source server able to serve S3 buckets. It can be deployed in cluster mode and is compatible with several storage backend. OpenCTI is compatible with any tool following the S3 standard.

"},{"location":"deployment/clustering/#platform","title":"Platform","text":"

As showed on the schema, best practices for cluster mode and to avoid any congestion in the technological stack are:

  • Deploy platform(s) dedicated to end users and connectors registration
  • Deploy platform(s) dedicated to workers / ingestion process
    • We recommend 3 to 4 workers maximum by OpenCTI instance.
    • The ingestion platforms will never be accessed directly by end users.

When enabling clustering, the number of nodes is displayed in Settings > Parameters.

"},{"location":"deployment/clustering/#managers-and-schedulers","title":"Managers and schedulers","text":"

Also, since some managers like the rule engine, the task manager and the notification manager can take some resources in the OpenCTI NodeJS process, it is highly recommended to disable them in the frontend cluster. OpenCTI automatically handle the distribution and the launching of the engines across all nodes in the cluster except where they are explicitly disabled in the configuration.

"},{"location":"deployment/configuration/","title":"Configuration","text":"

The purpose of this section is to learn how to configure OpenCTI to have it tailored for your production and development needs. It is possible to check all default parameters implemented in the platform in the default.json file.

Here are the configuration keys, for both containers (environment variables) and manual deployment.

Parameters equivalence

The equivalent of a config variable in environment variables is the usage of a double underscores (__) for a level of config.

For example:

\"providers\": {\n  \"ldap\": {\n    \"strategy\": \"LdapStrategy\"\n  }\n}\n

will become:

PROVIDERS__LDAP__STRATEGY=LdapStrategy\n

If you need to put a list of elements for the key, it must have a special formatting. Here is an example for redirect URIs for OpenID config:

\"PROVIDERS__OPENID__CONFIG__REDIRECT_URIS=[\\\"https://demo.opencti.io/auth/oic/callback\\\"]\"\n

"},{"location":"deployment/configuration/#platform","title":"Platform","text":""},{"location":"deployment/configuration/#api-frontend","title":"API & Frontend","text":""},{"location":"deployment/configuration/#basic-parameters","title":"Basic parameters","text":"Parameter Environment variable Default value Description app:port APP__PORT 4000 Listen port of the application app:base_path APP__BASE_PATH Specific URI (ie. /opencti) app:base_url APP__BASE_URL http://localhost:4000 Full URL of the platform (should include the base_path if any) app:request_timeout APP__REQUEST_TIMEOUT 1200000 Request timeout, in ms (default 20 minutes) app:session_timeout APP__SESSION_TIMEOUT 1200000 Session timeout, in ms (default 20 minutes) app:session_idle_timeout APP__SESSION_IDLE_TIMEOUT 0 Idle timeout (locking the screen), in ms (default 0 minute - disabled) app:session_cookie APP__SESSION_COOKIE false Use memory/session cookie instead of persistent one app:admin:email APP__ADMIN__EMAIL admin@opencti.io Default login email of the admin user app:admin:password APP__ADMIN__PASSWORD ChangeMe Default password of the admin user app:admin:token APP__ADMIN__TOKEN ChangeMe Default token (must be a valid UUIDv4) app:health_access_key APP__HEALTH_ACCESS_KEY ChangeMe Access key for the /health endpoint. Must be changed - will not respond to default value. Access with /health?health_access_key=ChangeMe"},{"location":"deployment/configuration/#network-and-security","title":"Network and security","text":"Parameter Environment variable Default value Description http_proxy HTTP_PROXY Proxy URL for HTTP connection (example: http://proxy:80080) https_proxy HTTPS_PROXY Proxy URL for HTTPS connection (example: http://proxy:80080) no_proxy NO_PROXY Comma separated list of hostnames for proxy exception (example: localhost,127.0.0.0/8,internal.opencti.io) app:https_cert:cookie_secure APP__HTTPS_CERT__COOKIE_SECURE false Set the flag \"secure\" for session cookies. app:https_cert:ca APP__HTTPS_CERT__CA Empty list [] Certificate authority paths or content, only if the client uses a self-signed certificate. app:https_cert:key APP__HTTPS_CERT__KEY Certificate key path or content app:https_cert:crt APP__HTTPS_CERT__CRT Certificate crt path or content app:https_cert:reject_unauthorized APP__HTTPS_CERT__REJECT_UNAUTHORIZED If not false, the server certificate is verified against the list of supplied CAs"},{"location":"deployment/configuration/#logging","title":"Logging","text":""},{"location":"deployment/configuration/#errors","title":"Errors","text":"Parameter Environment variable Default value Description app:app_logs:logs_level APP__APP_LOGS__LOGS_LEVEL info The application log level app:app_logs:logs_files APP__APP_LOGS__LOGS_FILES true If application logs is logged into files app:app_logs:logs_console APP__APP_LOGS__LOGS_CONSOLE true If application logs is logged to console (useful for containers) app:app_logs:logs_max_files APP__APP_LOGS__LOGS_MAX_FILES 7 Maximum number of daily files in logs app:app_logs:logs_directory APP__APP_LOGS__LOGS_DIRECTORY ./logs File logs directory"},{"location":"deployment/configuration/#audit","title":"Audit","text":"Parameter Environment variable Default value Description app:audit_logs:logs_files APP__AUDIT_LOGS__LOGS_FILES true If audit logs is logged into files app:audit_logs:logs_console APP__AUDIT_LOGS__LOGS_CONSOLE true If audit logs is logged to console (useful for containers) app:audit_logs:logs_max_files APP__AUDIT_LOGS__LOGS_MAX_FILES 7 Maximum number of daily files in logs app:audit_logs:logs_directory APP__AUDIT_LOGS__LOGS_DIRECTORY ./logs Audit logs directory"},{"location":"deployment/configuration/#telemetry","title":"Telemetry","text":"Parameter Environment variable Default value Description app:telemetry:metrics:enabled APP__TELEMETRY__METRICS__ENABLED false Enable the metrics collection. app:telemetry:metrics:exporter_otlp APP__TELEMETRY__METRICS__EXPORTER_OTLP Port to expose the OTLP endpoint. app:telemetry:metrics:exporter_prometheus APP__TELEMETRY__METRICS__EXPORTER_PROMETHEUS 14269 Port to expose the Prometheus endpoint."},{"location":"deployment/configuration/#maps-references","title":"Maps & references","text":"Parameter Environment variable Default value Description app:map_tile_server_dark APP__MAP_TILE_SERVER_DARK https://map.opencti.io/styles/filigran-dark2/{z}/{x}/{y}.png The address of the OpenStreetMap provider with dark theme style app:map_tile_server_light APP__MAP_TILE_SERVER_LIGHT https://map.opencti.io/styles/filigran-light2/{z}/{x}/{y}.png The address of the OpenStreetMap provider with light theme style app:reference_attachment APP__REFERENCE_ATTACHMENT false External reference mandatory attachment"},{"location":"deployment/configuration/#functional-customization","title":"Functional customization","text":"Parameter Environment variable Default value Description app:artifact_zip_password APP__ARTIFACT_ZIP_PASSWORD infected Artifact encrypted archive default password relations_deduplication:past_days RELATIONS_DEDUPLICATION__PAST_DAYS 30 De-duplicate relations based on start_time and stop_time - n days relations_deduplication:next_days RELATIONS_DEDUPLICATION__NEXT_DAYS 30 De-duplicate relations based on start_time and stop_time + n days relations_deduplication:created_by_based RELATIONS_DEDUPLICATION__CREATED_BY_BASED false Take into account the author to duplicate even if stat_time / stop_time are matching relations_deduplication:types_overrides:relationship_type:past_days RELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__PAST_DAYS Override the past days for a specific type of relationship (ex. targets) relations_deduplication:types_overrides:relationship_type:next_days RELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__NEXT_DAYS Override the next days for a specific type of relationship (ex. targets) relations_deduplication:types_overrides:relationship_type:created_by_based RELATIONS_DEDUPLICATION__RELATIONSHIP_TYPE__CREATED_BY_BASED Override the author duplication for a specific type of relationship (ex. targets)"},{"location":"deployment/configuration/#technical-customization","title":"Technical customization","text":"Parameter Environment variable Default value Description app:graphql:playground:enabled APP__GRAPHQL__PLAYGROUND__ENABLED true Enable the playground on /graphql app:graphql:playground:force_disabled_introspection APP__GRAPHQL__PLAYGROUND__FORCE_DISABLED_INTROSPECTION true Introspection is allowed to auth users but can be disabled in needed app:concurrency:retry_count APP__CONCURRENCY__RETRY_COUNT 200 Number of try to get the lock to work an element (create/update/merge, ...) app:concurrency:retry_delay APP__CONCURRENCY__RETRY_DELAY 100 Delay between 2 lock retry (in milliseconds) app:concurrency:retry_jitter APP__CONCURRENCY__RETRY_JITTER 50 Random jitter to prevent concurrent retry (in milliseconds) app:concurrency:max_ttl APP__CONCURRENCY__MAX_TTL 30000 Global maximum time for lock retry (in milliseconds)"},{"location":"deployment/configuration/#dependencies","title":"Dependencies","text":""},{"location":"deployment/configuration/#xtm-suite","title":"XTM Suite","text":"Parameter Environment variable Default value Description xtm:openbas_url XTM__OPENBAS_URL OpenBAS URL xtm:openbas_token XTM__OPENBAS_TOKEN OpenBAS token xtm:openbas_reject_unauthorized XTM__OPENBAS_REJECT_UNAUTHORIZED false Enable TLS certificate check xtm:openbas_disable_display XTM__OPENBAS_DISABLE_DISPLAY false Disable OpenBAS posture in the UI"},{"location":"deployment/configuration/#elasticsearch","title":"ElasticSearch","text":"Parameter Environment variable Default value Description elasticsearch:engine_selector ELASTICSEARCH__ENGINE_SELECTOR auto elk or opensearch, default is auto, please put elk if you use token auth. elasticsearch:engine_check ELASTICSEARCH__ENGINE_CHECK false Disable Search Engine compatibility matrix verification. Caution: OpenCTI was developed in compliance with the compatibility matrix. Setting the parameter to true may result in negative impacts. elasticsearch:url ELASTICSEARCH__URL http://localhost:9200 URL(s) of the ElasticSearch (supports http://user:pass@localhost:9200 and list of URLs) elasticsearch:username ELASTICSEARCH__USERNAME Username can be put in the URL or with this parameter elasticsearch:password ELASTICSEARCH__PASSWORD Password can be put in the URL or with this parameter elasticsearch:api_key ELASTICSEARCH__API_KEY API key for ElasticSearch token auth. Please set also engine_selector to elk elasticsearch:index_prefix ELASTICSEARCH__INDEX_PREFIX opencti Prefix for the indices elasticsearch:ssl:reject_unauthorized ELASTICSEARCH__SSL__REJECT_UNAUTHORIZED true Enable TLS certificate check elasticsearch:ssl:ca ELASTICSEARCH__SSL__CA Custom certificate path or content elasticsearch:search_wildcard_prefix ELASTICSEARCH__SEARCH_WILDCARD_PREFIX false Search includes words with automatic fuzzy comparison elasticsearch:search_fuzzy ELASTICSEARCH__SEARCH_FUZZY false Search will include words not starting with the search keyword"},{"location":"deployment/configuration/#redis","title":"Redis","text":"Parameter Environment variable Default value Description redis:mode REDIS__MODE single Connect to redis in \"single\", \"sentinel or \"cluster\" mode redis:namespace REDIS__NAMESPACE Namespace (to use as prefix) redis:hostname REDIS__HOSTNAME localhost Hostname of the Redis Server redis:hostnames REDIS__HOSTNAMES Hostnames definition for Redis cluster or sentinel mode: a list of host:port objects. redis:port REDIS__PORT 6379 Port of the Redis Server redis:sentinel_master_name REDIS__SENTINEL_MASTER_NAME Name of your Redis Sentinel Master (mandatory in sentinel mode) redis:use_ssl REDIS__USE_SSL false Is the Redis Server has TLS enabled redis:username REDIS__USERNAME Username of the Redis Server redis:password REDIS__PASSWORD Password of the Redis Server redis:ca REDIS__CA [] List of path(s) of the CA certificate(s) redis:trimming REDIS__TRIMMING 2000000 Number of elements to maintain in the stream. (0 = unlimited)"},{"location":"deployment/configuration/#rabbitmq","title":"RabbitMQ","text":"Parameter Environment variable Default value Description rabbitmq:hostname RABBITMQ__HOSTNAME localhost 7 Hostname of the RabbitMQ server rabbitmq:port RABBITMQ__PORT 5672 Port of the RabbitMQ server rabbitmq:port_management RABBITMQ__PORT_MANAGEMENT 15672 Port of the RabbitMQ Management Plugin rabbitmq:username RABBITMQ__USERNAME guest RabbitMQ user rabbitmq:password RABBITMQ__PASSWORD guest RabbitMQ password rabbitmq:queue_type RABBITMQ__QUEUE_TYPE \"classic\" RabbitMQ Queue Type (\"classic\" or \"quorum\") - - - - rabbitmq:use_ssl RABBITMQ__USE_SSL false Use TLS connection rabbitmq:use_ssl_cert RABBITMQ__USE_SSL_CERT Path or cert content rabbitmq:use_ssl_key RABBITMQ__USE_SSL_KEY Path or key content rabbitmq:use_ssl_pfx RABBITMQ__USE_SSL_PFX Path or pfx content rabbitmq:use_ssl_ca RABBITMQ__USE_SSL_CA [] List of path(s) of the CA certificate(s) rabbitmq:use_ssl_passphrase RABBITMQ__SSL_PASSPHRASE Passphrase for the key certificate rabbitmq:use_ssl_reject_unauthorized RABBITMQ__SSL_REJECT_UNAUTHORIZED false Reject rabbit self signed certificate - - - - rabbitmq:management_ssl RABBITMQ__MANAGEMENT_SSL false Is the Management Plugin has TLS enabled rabbitmq:management_ssl_reject_unauthorized RABBITMQ__SSL_REJECT_UNAUTHORIZED true Reject management self signed certificate"},{"location":"deployment/configuration/#s3-bucket","title":"S3 Bucket","text":"Parameter Environment variable Default value Description minio:endpoint MINIO__ENDPOINT localhost Hostname of the S3 Service. Example if you use AWS Bucket S3: s3.us-east-1.amazonaws.com (if minio:bucket_region value is us-east-1). This parameter value can be omitted if you use Minio as an S3 Bucket Service. minio:port MINIO__PORT 9000 Port of the S3 Service. For AWS Bucket S3 over HTTPS, this value can be changed (usually 443). minio:use_ssl MINIO__USE_SSL false Indicates whether the S3 Service has TLS enabled. For AWS Bucket S3 over HTTPS, this value could be true. minio:access_key MINIO__ACCESS_KEY ChangeMe Access key for the S3 Service. minio:secret_key MINIO__SECRET_KEY ChangeMe Secret key for the S3 Service. minio:bucket_name MINIO__BUCKET_NAME opencti-bucket S3 bucket name. Useful to change if you use AWS. minio:bucket_region MINIO__BUCKET_REGION us-east-1 Region of the S3 bucket if you are using AWS. This parameter value can be omitted if you use Minio as an S3 Bucket Service. minio:use_aws_role MINIO__USE_AWS_ROLE false Indicates whether to use AWS role auto credentials. When this parameter is configured, the minio:access_key and minio:secret_key parameters are not necessary."},{"location":"deployment/configuration/#smtp-service","title":"SMTP Service","text":"Parameter Environment variable Default value Description smtp:hostname SMTP__HOSTNAME SMTP Server hostname smtp:port SMTP__PORT 465 SMTP Port (25 or 465 for TLS) smtp:use_ssl SMTP__USE_SSL false SMTP over TLS smtp:reject_unauthorized SMTP__REJECT_UNAUTHORIZED false Enable TLS certificate check smtp:username SMTP__USERNAME SMTP Username if authentication is needed smtp:password SMTP__PASSWORD SMTP Password if authentication is needed"},{"location":"deployment/configuration/#ai-service","title":"AI Service","text":"

AI deployment and cloud services

There are several possibilities for Enterprise Edition customers to use OpenCTI AI endpoints:

  • Use the Filigran AI Service leveraging our custom AI model using the token given by the support team.
  • Use OpenAI or MistralAI cloud endpoints using your own tokens.
  • Deploy or use local AI endpoints (Filigran can provide you with the custom model).
Parameter Environment variable Default value Description ai:enabled AI__ENABLED true Enable AI capabilities ai:type AI__TYPE mistralai AI type (mistralai or openai) ai:endpoint AI__ENDPOINT Endpoint URL (empty means default cloud service) ai:token AI__TOKEN Token for endpoint credentials ai:model AI__MODEL Model to be used for text generation (depending on type) ai:model_images AI__MODEL_IMAGES Model to be used for image generation (depending on type)"},{"location":"deployment/configuration/#using-a-credentials-provider","title":"Using a credentials provider","text":"

In some cases, it may not be possible to put directly dependencies credentials directly in environment variables or static configuration. The platform can then retrieve them from a credentials provider. Here is the list of supported providers:

Credentials provider Provider key CyberArk cyberark

For each dependency, special configuration keys are available to ensure the platform retrieves credentials during start process. Not all dependencies support this mechanism, here is the exhaustive list:

Dependency Prefix ElasticSearch elasticsearch S3 Storage minio Redis redis OpenID secrets oic"},{"location":"deployment/configuration/#common-configurations","title":"Common configurations","text":"Parameter Environment variable Default value Description {prefix}:credentials_provider:https_cert:reject_unauthorized {PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__REJECT_UNAUTHORIZED false Reject unauthorized TLS connection {prefix}:credentials_provider:https_cert:crt {PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__CRT Path to the HTTPS certificate {prefix}:credentials_provider:https_cert:key {PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__KEY Path to the HTTPS key {prefix}:credentials_provider:https_cert:ca {PREFIX}__CREDENTIALS_PROVIDER__HTTPS_CERT__CA Path to the HTTPS CA certificate"},{"location":"deployment/configuration/#cyberark","title":"CyberArk","text":"Parameter Environment variable Default value Description {prefix}:credentials_provider:cyberark:uri {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__URI The URL of the CyberArk endpoint for credentials retrieval (GET request) {prefix}:credentials_provider:cyberark:app_id {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__APP_ID The used application ID for the dependency within CyberArk {prefix}:credentials_provider:cyberark:safe {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__SAFE The used safe key for the dependency within CyberArk {prefix}:credentials_provider:cyberark:object {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__OBJECT The used object key for the dependency within CyberArk {prefix}:credentials_provider:cyberark:default_splitter {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER : Default splitter of the credentials results, for \"username:password\", default is \":\" {prefix}:credentials_provider:cyberark:field_targets {PREFIX}__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS [] Fields targets in the data content response after splitting

Here is an example for ElasticSearch:

Environment variables:

- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__URI=http://my.cyberark.com/AIMWebService/api/Accounts\n- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__APP_ID=opencti-elastic\n- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__SAFE=mysafe-key\n- ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__OBJECT=myobject-key\n- \"ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER=:\" # As default is already \":\", may not be necessary\n- \"ELASTICSEARCH__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS=[\\\"username\\\",\\\"password\\\"]\"\n

JSON version:

\"elasticsearch\": {\n    \"credentials_provider\": {\n        \"cyberark\": {\n            \"uri\": \"http://my.cyberark.com/AIMWebService/api/Accounts\",\n            \"app_id\": \"opencti-elastic\",\n            \"safe\": \"mysafe-key\",\n            \"object\": \"myobject-key\",\n            \"default_splitter\": \":\",\n            \"field_targets\": [\"username\", \"password\"]\n      }\n    }\n}\n

Another example for MinIo (S3) using certificate:

Environment variables:

- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__CRT=/cert_volume/mycert.crt\n- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__KEY=/cert_volume/mycert.key\n- MINIO__CREDENTIALS_PROVIDER__HTTPS_CERT__CA=/cert_volume/ca.crt\n- MINIO__CREDENTIALS_PROVIDER__CYBERARK__URI=http://my.cyberark.com/AIMWebService/api/Accounts\n- MINIO__CREDENTIALS_PROVIDER__CYBERARK__APP_ID=opencti-s3\n- MINIO__CREDENTIALS_PROVIDER__CYBERARK__SAFE=mysafe-key\n- MINIO__CREDENTIALS_PROVIDER__CYBERARK__OBJECT=myobject-key\n- \"MINIO__CREDENTIALS_PROVIDER__CYBERARK__DEFAULT_SPLITTER=:\" # As default is already \":\", may not be necessary\n- \"MINIO__CREDENTIALS_PROVIDER__CYBERARK__FIELD_TARGETS=[\\\"access_key\\\",\\\"secret_key\\\"]\"\n

"},{"location":"deployment/configuration/#engines-schedules-and-managers","title":"Engines, Schedules and Managers","text":"Parameter Environment variable Default value Description rule_engine:enabled RULE_ENGINE__ENABLED true Enable/disable the rule engine rule_engine:lock_key RULE_ENGINE__LOCK_KEY rule_engine_lock Lock key of the engine in Redis - - - - history_manager:enabled HISTORY_MANAGER__ENABLED true Enable/disable the history manager history_manager:lock_key HISTORY_MANAGER__LOCK_KEY history_manager_lock Lock key for the manager in Redis - - - - task_scheduler:enabled TASK_SCHEDULER__ENABLED true Enable/disable the task scheduler task_scheduler:lock_key TASK_SCHEDULER__LOCK_KEY task_manager_lock Lock key for the scheduler in Redis task_scheduler:interval TASK_SCHEDULER__INTERVAL 10000 Interval to check new task to do (in ms) - - - - sync_manager:enabled SYNC_MANAGER__ENABLED true Enable/disable the sync manager sync_manager:lock_key SYNC_MANAGER__LOCK_KEY sync_manager_lock Lock key for the manager in Redis sync_manager:interval SYNC_MANAGER__INTERVAL 10000 Interval to check new sync feeds to consume (in ms) - - - - expiration_scheduler:enabled EXPIRATION_SCHEDULER__ENABLED true Enable/disable the scheduler expiration_scheduler:lock_key EXPIRATION_SCHEDULER__LOCK_KEY expired_manager_lock Lock key for the scheduler in Redis expiration_scheduler:interval EXPIRATION_SCHEDULER__INTERVAL 300000 Interval to check expired indicators (in ms) - - - - retention_manager:enabled RETENTION_MANAGER__ENABLED true Enable/disable the retention manager retention_manager:lock_key RETENTION_MANAGER__LOCK_KEY retention_manager_lock Lock key for the manager in Redis retention_manager:interval RETENTION_MANAGER__INTERVAL 60000 Interval to check items to be deleted (in ms) - - - - notification_manager:enabled NOTIFICATION_MANAGER__ENABLED true Enable/disable the notification manager notification_manager:lock_live_key NOTIFICATION_MANAGER__LOCK_LIVE_KEY notification_live_manager_lock Lock live key for the manager in Redis notification_manager:lock_digest_key NOTIFICATION_MANAGER__LOCK_DIGEST_KEY notification_digest_manager_lock Lock digest key for the manager in Redis notification_manager:interval NOTIFICATION_MANAGER__INTERVAL 10000 Interval to push notifications - - - - publisher_manager:enabled PUBLISHER_MANAGER__ENABLED true Enable/disable the publisher manager publisher_manager:lock_key PUBLISHER_MANAGER__LOCK_KEY publisher_manager_lock Lock key for the manager in Redis publisher_manager:interval PUBLISHER_MANAGER__INTERVAL 10000 Interval to send notifications / digests (in ms) - - - - ingestion_manager:enabled INGESTION_MANAGER__ENABLED true Enable/disable the ingestion manager ingestion_manager:lock_key INGESTION_MANAGER__LOCK_KEY ingestion_manager_lock Lock key for the manager in Redis ingestion_manager:interval INGESTION_MANAGER__INTERVAL 300000 Interval to check for new data in remote feeds - - - - playbook_manager:enabled PLAYBOOK_MANAGER__ENABLED true Enable/disable the playbook manager playbook_manager:lock_key PLAYBOOK_MANAGER__LOCK_KEY publisher_manager_lock Lock key for the manager in Redis playbook_manager:interval PLAYBOOK_MANAGER__INTERVAL 60000 Interval to check new playbooks - - - - activity_manager:enabled ACTIVITY_MANAGER__ENABLED true Enable/disable the activity manager activity_manager:lock_key ACTIVITY_MANAGER__LOCK_KEY activity_manager_lock Lock key for the manager in Redis - - - - connector_manager:enabled CONNECTOR_MANAGER__ENABLED true Enable/disable the connector manager connector_manager:lock_key CONNECTOR_MANAGER__LOCK_KEY connector_manager_lock Lock key for the manager in Redis connector_manager:works_day_range CONNECTOR_MANAGER__WORKS_DAY_RANGE 7 Days range before considering the works as too old connector_manager:interval CONNECTOR_MANAGER__INTERVAL 10000 Interval to check the state of the works - - - - import_csv_built_in_connector:enabled IMPORT_CSV_CONNECTOR__ENABLED true Enable/disable the csv import connector import_csv_built_in_connector:validate_before_import IMPORT_CSV_CONNECTOR__VALIDATE_BEFORE_IMPORT false Validates the bundle before importing - - - - file_index_manager:enabled FILE_INDEX_MANAGER__ENABLED true Enable/disable the file indexing manager file_index_manager:stream_lock_key FILE_INDEX_MANAGER__STREAM_LOCK file_index_manager_stream_lock Stream lock key for the manager in Redis file_index_manager:interval FILE_INDEX_MANAGER__INTERVAL 60000 Interval to check for new files - - - - indicator_decay_manager:enabled INDICATOR_DECAY_MANAGER__ENABLED true Enable/disable the indicator decay manager indicator_decay_manager:lock_key INDICATOR_DECAY_MANAGER__LOCK_KEY indicator_decay_manager_lock Lock key for the manager in Redis indicator_decay_manager:interval INDICATOR_DECAY_MANAGER__INTERVAL 60000 Interval to check for indicators to update indicator_decay_manager:batch_size INDICATOR_DECAY_MANAGER__BATCH_SIZE 10000 Number of indicators handled by the manager - - - - garbage_collection_manager:enabled GARBAGE_COLLECTION_MANAGER__ENABLED true Enable/disable the trash manager garbage_collection_manager:lock_key GARBAGE_COLLECTION_MANAGER__LOCK_KEY garbage_collection_manager_lock Lock key for the manager in Redis garbage_collection_manager:interval GARBAGE_COLLECTION_MANAGER__INTERVAL 60000 Interval to check for trash elements to delete garbage_collection_manager:batch_size GARBAGE_COLLECTION_MANAGER__BATCH_SIZE 10000 Number of trash elements to delete at once garbage_collection_manager:deleted_retention_days GARBAGE_COLLECTION_MANAGER__DELETED_RETENTION_DAYS 7 Days after which elements in trash are deleted - - - - telemetry_manager:lock_key TELEMETRY_MANAGER__LOCK_LOCK telemetry_manager_lock Lock key for the manager in Redis

Manager's duties

A description of each manager's duties is available on a dedicated page.

"},{"location":"deployment/configuration/#worker-and-connector","title":"Worker and connector","text":"

Can be configured manually using the configuration file config.yml or through environment variables.

Parameter Environment variable Default value Description opencti:url OPENCTI_URL The URL of the OpenCTI platform opencti:token OPENCTI_TOKEN A token of an administrator account with bypass capability - - - - mq:use_ssl / / Depending of the API configuration (fetch from API) mq:use_ssl_ca MQ_USE_SSL_CA Path or ca content mq:use_ssl_cert MQ_USE_SSL_CERT Path or cert content mq:use_ssl_key MQ_USE_SSL_KEY Path or key content mq:use_ssl_passphrase MQ_USE_SSL_PASSPHRASE Passphrase for the key certificate mq:use_ssl_reject_unauthorized MQ_USE_SSL_REJECT_UNAUTHORIZED false Reject rabbit self signed certificate"},{"location":"deployment/configuration/#worker-specific-configuration","title":"Worker specific configuration","text":""},{"location":"deployment/configuration/#logging_1","title":"Logging","text":"Parameter Environment variable Default value Description worker:log_level WORKER_LOG_LEVEL info The log level (error, warning, info or debug)"},{"location":"deployment/configuration/#telemetry_1","title":"Telemetry","text":"Parameter Environment variable Default value Description worker:telemetry_enabled WORKER_TELEMETRY_ENABLED false Enable the Prometheus endpoint worker:telemetry_prometheus_port WORKER_PROMETHEUS_TELEMETRY_PORT 14270 Port of the Prometheus endpoint worker:telemetry_prometheus_host WORKER_PROMETHEUS_TELEMETRY_HOST 0.0.0.0 Listen address of the Prometheus endpoint"},{"location":"deployment/configuration/#connector-specific-configuration","title":"Connector specific configuration","text":"

For specific connector configuration, you need to check each connector behavior.

"},{"location":"deployment/configuration/#elasticsearch_1","title":"ElasticSearch","text":"

If you want to adapt the memory consumption of ElasticSearch, you can use these options:

# Add the following environment variable:\n\"ES_JAVA_OPTS=-Xms8g -Xmx8g\"\n

This can be done in configuration file in the jvm.conf file.

"},{"location":"deployment/connectors/","title":"Connectors","text":""},{"location":"deployment/connectors/#introduction","title":"Introduction","text":"

Connectors list

You are looking for the available connectors? The list is in the OpenCTI Ecosystem.

Connectors are the cornerstone of the OpenCTI platform and allow organizations to easily ingest, enrich or export data. According to their functionality and use case, they are categorized in the following classes.

"},{"location":"deployment/connectors/#import","title":"Import","text":"

These connectors automatically retrieve information from an external organization, application, or service, and convert it to STIX 2.1 bundles. Then, they import it into OpenCTI using the workers.

"},{"location":"deployment/connectors/#enrichment","title":"Enrichment","text":"

When a new object is created in the platform or on the user request, it is possible to trigger the internal enrichment connector to lookup and/or search the object in external organizations, applications, or services. If the object is found, the connectors will generate a STIX 2.1 bundle which will increase the level of knowledge about the concerned object.

"},{"location":"deployment/connectors/#stream","title":"Stream","text":"

These connectors connect to a platform live stream and continuously do something with the received events. In most cases, they are used to consume OpenCTI data and insert them in third-party platforms such as SIEMs, XDRs, EDRs, etc. In some cases, stream connectors can also query the external system on a regular basis and act as import connector for instance to gather alerts and sightings related to CTI data and push them to OpenCTI (bi-directional).

"},{"location":"deployment/connectors/#import-files","title":"Import files","text":"

Information from an uploaded file can be extracted and ingested into OpenCTI. Examples are files attached to a report or a STIX 2.1 file.

"},{"location":"deployment/connectors/#export-files","title":"Export files","text":"

Information stored in OpenCTI can be extracted into different file formats like .csv or .json (STIX 2.1).

"},{"location":"deployment/connectors/#connector-configuration","title":"Connector configuration","text":""},{"location":"deployment/connectors/#connector-users-and-tokens","title":"Connector users and tokens","text":"

All connectors have to be able to access the OpenCTI API. To allow this connection, they have 2 mandatory configuration parameters, the OPENCTI_URL and the OPENCTI_TOKEN.

Connectors tokens

Be careful, we strongly recommend to use a dedicated token for each connector running in the platform. So you have to create a specific user for each of them.

Also, if all connectors users can run with a user belonging to the Connectors group (with the Connector role), the Internal Export Files should be run with a user who is Administrator (with bypass capability) because they impersonate the user requesting the export to avoid data leak.

Type Required role Used permissions EXTERNAL_IMPORT Connector Import data with the connector user. INTERNAL_ENRICHMENT Connector Enrich data with the connector user. INTERNAL_IMPORT_FILE Connector Import data with the connector user. INTERNAL_EXPORT_FILE Administrator Export data with the user who requested the export. STREAM Connector Consume the streams with the connector user."},{"location":"deployment/connectors/#parameters","title":"Parameters","text":"

In addition to these 2 parameters, connectors have other mandatory parameters that need to be set in order to get them work.

Here is an example of a connector docker-compose.yml file:

- CONNECTOR_ID=ChangeMe\n- CONNECTOR_TYPE=EXTERNAL_IMPORT\n- CONNECTOR_NAME=MITRE ATT&CK\n- CONNECTOR_SCOPE=identity,attack-pattern,course-of-action,intrusion-set,malware,tool,report\n- CONNECTOR_LOG_LEVEL=info\n

Here is an example in a connector config.yml file:

connector:\n  id: 'ChangeMe'\n  type: 'EXTERNAL_IMPORT'\n  name: 'MITRE ATT&CK'\n  scope: 'identity,attack-pattern,course-of-action,intrusion-set,malware,tool,report'\n  log_level: 'info'\n
"},{"location":"deployment/connectors/#advanced-parameters","title":"Advanced parameters","text":"

By default, connectors are connecting to RabbitMQ using parameters and credentials directly given by the API during the connector registration process. In some cases, you may need to override them.

- MQ_HOST=rabbit.mydomain.com\n- MQ_PORT=5672\n- MQ_VHOST=/\n- MQ_USE_SSL=false\n- MQ_USER=guest\n- MQ_PASS=guest\n

Here is an example in a connector config.yml file:

mq:\n  host: 'rabbit.mydomain.com'\n  port: '5672'\n  use_ssl: false\n  user: 'guest'\n  pass: 'guest'\n
"},{"location":"deployment/connectors/#networking","title":"Networking","text":"

Be aware that all connectors are reaching RabbitMQ based the RabbitMQ configuration provided by the OpenCTI platform. The connector must be able to reach RabbitMQ on the specified hostname and port. If you have a specific Docker network configuration, please be sure to adapt your docker-compose.yml file in such way that the connector container gets attached to the OpenCTI Network, e.g.:

networks:\n  default:\n    external: true\n    name: opencti-docker_default\n

"},{"location":"deployment/connectors/#connector-token","title":"Connector token","text":""},{"location":"deployment/connectors/#create-the-user","title":"Create the user","text":"

As mentioned previously, it is strongly recommended to run each connector with its own user. The Internal Export File connectors should be launched with a user that belongs to a group which has an \u201cAdministrator\u201d role (with bypass all capabilities enabled).

By default, in platform, a group named \"Connectors\" already exists. So just create a new user with the name [C] Name of the connector in Settings > Security > Users.

"},{"location":"deployment/connectors/#put-the-user-in-the-group","title":"Put the user in the group","text":"

Just go to the user you have just created and add it to the Connectors group.

Then just get the token of the user displayed in the interface.

"},{"location":"deployment/connectors/#docker-activation","title":"Docker activation","text":"

You can either directly run the Docker image of connectors or add them to your current docker-compose.yml file.

"},{"location":"deployment/connectors/#add-a-connector-to-your-deployment","title":"Add a connector to your deployment","text":"

For instance, to enable the MISP connector, you can add a new service to your docker-compose.yml file:

  connector-misp:\n    image: opencti/connector-misp:latest\n    environment:\n      - OPENCTI_URL=http://localhost\n      - OPENCTI_TOKEN=ChangeMe\n      - CONNECTOR_ID=ChangeMe\n      - CONNECTOR_TYPE=EXTERNAL_IMPORT\n      - CONNECTOR_NAME=MISP\n      - CONNECTOR_SCOPE=misp\n      - CONNECTOR_LOG_LEVEL=info\n      - MISP_URL=http://localhost # Required\n      - MISP_KEY=ChangeMe # Required\n      - MISP_SSL_VERIFY=False # Required\n      - MISP_CREATE_REPORTS=True # Required, create report for MISP event\n      - MISP_REPORT_CLASS=MISP event # Optional, report_class if creating report for event\n      - MISP_IMPORT_FROM_DATE=2000-01-01 # Optional, import all event from this date\n      - MISP_IMPORT_TAGS=opencti:import,type:osint # Optional, list of tags used for import events\n      - MISP_INTERVAL=1 # Required, in minutes\n    restart: always\n
"},{"location":"deployment/connectors/#launch-a-standalone-connector","title":"Launch a standalone connector","text":"

To launch a standalone connector, you can use the docker-compose.yml file of the connector itself. Just download the latest release and start the connector:

$ wget https://github.com/OpenCTI-Platform/connectors/archive/{RELEASE_VERSION}.zip\n$ unzip {RELEASE_VERSION}.zip\n$ cd connectors-{RELEASE_VERSION}/misp/\n

Change the configuration in the docker-compose.yml according to the parameters of the platform and of the targeted service. Then launch the connector:

$ docker-compose up\n
"},{"location":"deployment/connectors/#manual-activation","title":"Manual activation","text":"

If you want to manually launch connector, you just have to install Python 3 and pip3 for dependencies:

$ apt install python3 python3-pip\n

Download the release of the connectors:

$ wget <https://github.com/OpenCTI-Platform/connectors/archive/{RELEASE_VERSION}.zip>\n$ unzip {RELEASE_VERSION}.zip\n$ cd connectors-{RELEASE_VERSION}/misp/src/\n

Install dependencies and initialize the configuration:

$ pip3 install -r requirements.txt\n$ cp config.yml.sample config.yml\n

Change the config.yml content according to the parameters of the platform and of the targeted service and launch the connector:

$ python3 misp.py\n
"},{"location":"deployment/connectors/#connectors-status","title":"Connectors status","text":"

The connector status can be displayed in the dedicated section of the platform available in Data > Ingestion > Connectors. You will be able to see the statistics of the RabbitMQ queue of the connector:

Problem

If you encounter problems deploying OpenCTI or connectors, you can consult the troubleshooting page.

"},{"location":"deployment/installation/","title":"Installation","text":"

All components of OpenCTI are shipped both as Docker images and manual installation packages.

Production deployment

For production deployment, we recommend to deploy all components in containers, including dependencies, using native cloud services or orchestration systems such as Kubernetes.

To have more details about deploying OpenCTI and its dependencies in cluster mode, please read the dedicated section.

  • Use Docker

    Deploy OpenCTI using Docker and the default docker-compose.yml provided in the docker.

    Setup

  • Manual installation

    Deploy dependencies and launch the platform manually using the packages released in the GitHub releases.

    Explore

"},{"location":"deployment/installation/#using-docker","title":"Using Docker","text":"

OpenCTI can be deployed using the docker-compose command.

Deploy FIPS 140-2 compliant components

We provide FIPS 140-2 compliant images. Please read the dedicated documentation to understand how to deploy OpenCTI in FIPS-compliant mode.

"},{"location":"deployment/installation/#pre-requisites","title":"Pre-requisites","text":"

Linux

sudo apt install docker-compose\n

Windows and MacOS

Just download the appropriate Docker for Desktop version for your operating system.

"},{"location":"deployment/installation/#clone-the-repository","title":"Clone the repository","text":"

Docker helpers are available in the Docker GitHub repository.

mkdir -p /path/to/your/app && cd /path/to/your/app\ngit clone https://github.com/OpenCTI-Platform/docker.git\ncd docker\n
"},{"location":"deployment/installation/#configure-the-environment","title":"Configure the environment","text":"

ElasticSearch / OpenSearch configuration

  • We strongly recommend that you add the following ElasticSearch / OpenSearch parameter:
thread_pool.search.queue_size=5000\n
  • Check the OpenCTI Integration User Permissions in OpenSearch/ElasticSearch for detailed information about the user permissions required for the OpenSearch/ElasticSearch integration.

Before running the docker-compose command, the docker-compose.yml file should be configured. By default, the docker-compose.yml file is using environment variables available in the file .env.sample.

You can either rename the file .env.sample as .env and enter the values or just directly edit the docker-compose.yml with the values for your environment.

Configuration static parameters

The complete list of available static parameters is available in the configuration section.

Here is an example to quickly generate the .env file under Linux, especially all the default UUIDv4:

sudo apt install -y jq\ncd ~/docker\n(cat << EOF\nOPENCTI_ADMIN_EMAIL=admin@opencti.io\nOPENCTI_ADMIN_PASSWORD=ChangeMePlease\nOPENCTI_ADMIN_TOKEN=$(cat /proc/sys/kernel/random/uuid)\nOPENCTI_BASE_URL=http://localhost:8080\nMINIO_ROOT_USER=$(cat /proc/sys/kernel/random/uuid)\nMINIO_ROOT_PASSWORD=$(cat /proc/sys/kernel/random/uuid)\nRABBITMQ_DEFAULT_USER=guest\nRABBITMQ_DEFAULT_PASS=guest\nELASTIC_MEMORY_SIZE=4G\nCONNECTOR_HISTORY_ID=$(cat /proc/sys/kernel/random/uuid)\nCONNECTOR_EXPORT_FILE_STIX_ID=$(cat /proc/sys/kernel/random/uuid)\nCONNECTOR_EXPORT_FILE_CSV_ID=$(cat /proc/sys/kernel/random/uuid)\nCONNECTOR_IMPORT_FILE_STIX_ID=$(cat /proc/sys/kernel/random/uuid)\nCONNECTOR_EXPORT_FILE_TXT_ID=$(cat /proc/sys/kernel/random/uuid)\nCONNECTOR_IMPORT_DOCUMENT_ID=$(cat /proc/sys/kernel/random/uuid)\nSMTP_HOSTNAME=localhost\nEOF\n) > .env\n

If your docker-compose deployment does not support .env files, just export all environment variables before launching the platform:

export $(cat .env | grep -v \"#\" | xargs)\n

As OpenCTI has a dependency on ElasticSearch, you have to set vm.max_map_count before running the containers, as mentioned in the ElasticSearch documentation.

sudo sysctl -w vm.max_map_count=1048575\n

To make this parameter persistent, add the following to the end of your /etc/sysctl.conf:

vm.max_map_count=1048575\n
"},{"location":"deployment/installation/#persist-data","title":"Persist data","text":"

The default for OpenCTI data is to be persistent.

In docker-compose.yml, you will find the list of necessary persistent volumes for the dependencies at the end:

volumes:\n  esdata:     # ElasticSearch data\n  s3data:     # S3 bucket data\n  redisdata:  # Redis data\n  amqpdata:   # RabbitMQ data\n
"},{"location":"deployment/installation/#run-opencti","title":"Run OpenCTI","text":""},{"location":"deployment/installation/#using-single-node-docker","title":"Using single node Docker","text":"

After changing your .env file run docker-compose in detached (-d) mode:

sudo systemctl start docker.service\n# Run docker-compose in detached\ndocker-compose up -d\n
"},{"location":"deployment/installation/#using-docker-swarm","title":"Using Docker swarm","text":"

In order to have the best experience with Docker, we recommend using the Docker stack feature. In this mode you will have the capacity to easily scale your deployment.

# If your virtual machine is not a part of a Swarm cluster, please use:\ndocker swarm init\n

Put your environment variables in /etc/environment:

# If you already exported your variables to .env from above:\nsudo cat .env >> /etc/environment\nsudo bash -c 'cat .env >> /etc/environment'\nsudo docker stack deploy --compose-file docker-compose.yml opencti\n

Installation done

You can now go to http://localhost:8080 and log in with the credentials configured in your environment variables.

"},{"location":"deployment/installation/#manual-installation","title":"Manual installation","text":""},{"location":"deployment/installation/#prerequisites","title":"Prerequisites","text":""},{"location":"deployment/installation/#installation-of-dependencies","title":"Installation of dependencies","text":"

You have to install all the needed dependencies for the main application and the workers. The example below is for Debian-based systems:

sudo apt-get update\nsudo apt-get install build-essential nodejs npm python3 python3-pip python3-dev\n
"},{"location":"deployment/installation/#download-the-application-files","title":"Download the application files","text":"

First, you have to download and extract the latest release file. Then select the version to install depending of your operating system:

For Linux:

  • If your OS supports libc (Ubuntu, Debian, ...) you have to install the opencti-release_{RELEASE_VERSION}.tar.gz version.
  • If your OS uses musl (Alpine, ...) you have to install the opencti-release-{RELEASE_VERSION}_musl.tar.gz version.

For Windows:

We don't provide any Windows release for now. However it is still possible to check the code out, manually install the dependencies and build the software.

mkdir /path/to/your/app && cd /path/to/your/app\nwget <https://github.com/OpenCTI-Platform/opencti/releases/download/{RELEASE_VERSION}/opencti-release-{RELEASE_VERSION}.tar.gz>\ntar xvfz opencti-release-{RELEASE_VERSION}.tar.gz\n
"},{"location":"deployment/installation/#install-the-main-platform","title":"Install the main platform","text":""},{"location":"deployment/installation/#configure-the-application","title":"Configure the application","text":"

The main application has just one JSON configuration file to change and a few Python modules to install

cd opencti\ncp config/default.json config/production.json\n

Change the config/production.json file according to your configuration of ElasticSearch, Redis, RabbitMQ and S3 bucket as well as default credentials (the ADMIN_TOKEN must be a valid UUID).

"},{"location":"deployment/installation/#install-the-python-modules","title":"Install the Python modules","text":"
cd src/python\npip3 install -r requirements.txt\ncd ../..\n
"},{"location":"deployment/installation/#start-the-application","title":"Start the application","text":"

The application is just a NodeJS process, the creation of the database schema and the migration will be done at starting.

Please verify that yarn version is greater than 4 and node version is greater or equals to v19. Please note that some Node.js version are outdated in linux package manager, you can download a recent one in https://nodejs.org/en/download or alternatively nvm can help to chose a recent version of Node.js https://github.com/nvm-sh/nvm

yarn --version\n#4.1.0\nnode --version\n#v20.11.1\n

Once Node.js is setup, you can build and run with (from inside opencti folder):

yarn install\nyarn build\nyarn serv\n

The default username and password are those you have put in the config/production.json file.

"},{"location":"deployment/installation/#install-the-worker","title":"Install the worker","text":"

The OpenCTI worker is used to write the data coming from the RabbitMQ messages broker.

"},{"location":"deployment/installation/#configure-the-worker","title":"Configure the worker","text":"
cd worker\npip3 install -r requirements.txt\ncp config.yml.sample config.yml\n

Change the config.yml file according to your OpenCTI token.

"},{"location":"deployment/installation/#start-as-many-workers-as-you-need","title":"Start as many workers as you need","text":"
python3 worker.py &\npython3 worker.py &\n

Installation done

You can now go to http://localhost:4000 and log in with the credentials configured in your production.json file.

"},{"location":"deployment/installation/#community-contributions","title":"Community contributions","text":""},{"location":"deployment/installation/#terraform","title":"Terraform","text":"
  • Multi-clouds Terraform scripts

    This repository is here to provide you with a quick and easy way to deploy an OpenCTI instance in the cloud (AWS, Azure, or GCP).

    GitHub Respository

  • AWS Advanced Terraform scripts

    A Terraform deployment of OpenCTI designed to make use of native AWS Resources (where feasible). This includes AWS ECS Fargate, AWS OpenSearch, etc.

    GitHub Repository

"},{"location":"deployment/installation/#helm-charts","title":"Helm Charts","text":"
  • Kubernetes Helm Charts

    OpenCTI Helm Charts for Kubernetes with a global configuration file. More information how to deploy here on basic installation and examples.

    GitHub Repository

"},{"location":"deployment/installation/#deploy-behind-a-reverse-proxy","title":"Deploy behind a reverse proxy","text":"

If you want to use OpenCTI behind a reverse proxy with a context path, like https://domain.com/opencti, please change the base_path static parameter.

  • APP__BASE_PATH=/opencti

By default OpenCTI use websockets so don't forget to configure your proxy for this usage, an example with Nginx:

location / {\n    proxy_cache                 off;\n    proxy_buffering             off;\n    proxy_http_version          1.1;\n    proxy_set_header Upgrade    $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n    proxy_set_header Host       $host;\n    chunked_transfer_encoding   off;\n    proxy_pass                  http://YOUR_UPSTREAM_BACKEND;\n  }\n
"},{"location":"deployment/installation/#additional-memory-information","title":"Additional memory information","text":""},{"location":"deployment/installation/#platform","title":"Platform","text":"

OpenCTI platform is based on a NodeJS runtime, with a memory limit of 8GB by default. If you encounter OutOfMemory exceptions, this limit could be changed:

- NODE_OPTIONS=--max-old-space-size=8096\n
"},{"location":"deployment/installation/#workers-and-connectors","title":"Workers and connectors","text":"

OpenCTI workers and connectors are Python processes. If you want to limit the memory of the process, we recommend to directly use Docker to do that. You can find more information in the official Docker documentation.

"},{"location":"deployment/installation/#elasticsearch","title":"ElasticSearch","text":"

ElasticSearch is also a JAVA process. In order to setup the JAVA memory allocation, you can use the environment variable ES_JAVA_OPTS. You can find more information in the official ElasticSearch documentation.

"},{"location":"deployment/installation/#redis","title":"Redis","text":"

Redis has a very small footprint on keys but will consume memory for the stream. By default the size of the stream is limited to 2 millions which represents a memory footprint around 8 GB. You can find more information in the Redis docker hub.

"},{"location":"deployment/installation/#minio-s3-bucket","title":"MinIO / S3 Bucket","text":"

MinIO is a small process and does not require a high amount of memory. More information are available for Linux here on the Kernel tuning guide.

"},{"location":"deployment/installation/#rabbitmq","title":"RabbitMQ","text":"

The RabbitMQ memory configuration can be find in the RabbitMQ official documentation. RabbitMQ will consumed memory until a specific threshold, therefore it should be configure along with the Docker memory limitation.

"},{"location":"deployment/integrations/","title":"Integrations","text":""},{"location":"deployment/integrations/#introduction","title":"Introduction","text":"

OpenCTI supports multiple ways to integrate with other systems which do not have native connectors or plugins to the platform. Here are the technical features available to ease the connection and the integration of the platform with other applications.

Connectors list

If you are looking for the list of OpenCTI connectors or native integration, please check the OpenCTI Ecosystem.

"},{"location":"deployment/integrations/#native-feeds-and-streams","title":"Native feeds and streams","text":"

To ease integrations with other products, OpenCTI has built-in capabilities to deliver the data to third-parties.

"},{"location":"deployment/integrations/#csv-feeds","title":"CSV Feeds","text":"

It is possible to create as many CSV feeds as needed, based on filters and accessible in HTTP. CSV feeds are available in Data > Data sharing > CSV deeds.

When creating a CSV feed, you need to select one or multiple types of entities to make available. Then, you must assign a field (of an entity type) to each column in the CSV:

Details

For more information about CSV feeds, filters and configuration, please check the Native feeds page.

"},{"location":"deployment/integrations/#taxii-collections","title":"TAXII collections","text":"

Most of the modern cybersecurity systems such as SIEMs, EDRs, XDRs and even firewalls support the TAXII protocol which is basically a paginated HTTP STIX feed. OpenCTI implements a TAXII 2.1 server with the ability to create as many TAXII collections as needed in Data > Data sharing > TAXII Collections.

TAXII collections are a sub-selection of the knowledge available in the platform and rely on filters. For instance, it is possible to create TAXII collections for pieces of malware with a given label, for indicators with a score greater than n, etc.

"},{"location":"deployment/integrations/#live-streams","title":"Live Streams","text":"

After implementing CSV feeds and TAXII collections, we figured out that those 2 stateless APIs are definitely not enough when it comes to tackle advanced information sharing challenges such as:

  • Real time transmission of the information (i.e. avoid hundreds of systems to pull data every 5 minutes).
  • Dependencies resolution (i.e. an intrusion created by an organization but the organization is not in the TAXII collection).
  • Partial update for huge entities such as report (i.e. just having the update event).
  • Delete events when necessary (i.e. to handle indicators expiration in third party systems for instance).

That's why we've developed the live streams. They are available in Data > Data sharing > Live streams. As with TAXII collections, it is possible to create as many streams as needed using filters.

Streams implement the HTTP SSE (Server-sent events) protocol and give applications the possibility to consume a real time pure STIX 2.1 stream. Stream connectors in the OpenCTI Ecosystem are using live streams to consume data and do something such as create / update / delete information in SIEMs, XDRs, etc.

"},{"location":"deployment/integrations/#authentication","title":"Authentication","text":"

For all previously explained capabilities, as they are over the HTTP protocol, 3 authentication mechanisms are available to consume them.

  1. Using a bearer header with your OpenCTI API key

    Authorization: Bearer a17bc103-8420-4208-bd53-e1f80845d15f\n

    API Key

    Your API key can be found in your profile available clicking on the top right icon.

  2. Using basic authentication

    Username: Your platform username\nPassword: Your plafrom password\nAuthorization: Basic c2FtdWVsLmhhc3NpbmVBZmlsaWdyYW4uaW86TG91aXNlMTMwNCM=\n
  3. Using client certificate authentication

    To know how to configure the client certificate authentication, please consult the authentication configuration section.

"},{"location":"deployment/integrations/#api-and-libraries","title":"API and libraries","text":""},{"location":"deployment/integrations/#graphql-api","title":"GraphQL API","text":"

To allow analysts and developers to implement more custom or complex use cases, a full GraphQL API is available in the application on the /graphql endpoint.

The API can be queried using various GraphQL client such as Postman but you can leverage any HTTP client to forge GraphQL queries using POST methods.

"},{"location":"deployment/integrations/#authentication_1","title":"Authentication","text":"

The API authentication can be performed using the token of a user and a classic Authorization header:

Content-Type: application/json\nAuthorization: Bearer 6b6554c4-bb2c-4c80-9cd3-30288c8bf424\n
"},{"location":"deployment/integrations/#playground","title":"Playground","text":"

The playground is available on the /graphql endpoint. A link button is also available in the profile of your user.

All the schema documentation is directly available in the playground.

If you already logged to OpenCTI with the same browser you should be able to directly do some requests. If you are not authenticated or want to authenticate only through the playground you can use a header configuration using your profile token

Example of configuration (bottom left of the playground):

Additional GraphQL documentation

To find out more about GraphQL and the playground, you can find two additional documentation pages: the GraphQL API page and the GraphQL playground page.

"},{"location":"deployment/integrations/#python-library","title":"Python library","text":"

Since not everyone is familiar with GraphQL APIs, we've developed a Python library to ease the interaction with it. The library is pretty easy to use. To initiate the client:

# coding: utf-8\n\nfrom pycti import OpenCTIApiClient\n\n# Variables\napi_url = \"http://opencti:4000\"\napi_token = \"bfa014e0-e02e-4aa6-a42b-603b19dcf159\"\n\n# OpenCTI initialization\nopencti_api_client = OpenCTIApiClient(api_url, api_token)\n

Then just use the available helpers:

# Search for malware with the keyword \"windows\"\nmalwares = opencti_api_client.malware.list(search=\"windows\")\n\n# Print\nprint(malwares)\n

Details

For more detailed information about the Python library, please read the dedicated section.

"},{"location":"deployment/managers/","title":"Platform managers","text":"

Platform managers are background components that perform various tasks to support some important functionalities in the platform.

Here is a list of all the managers on the platform:

"},{"location":"deployment/managers/#rules-engine","title":"Rules engine","text":"

Allows users to execute pre-defined actions based on the data and events in the platform.

These rules are accessible in Settings > Customization > Rules engine.

The rules engine is designed to help users automate and streamline their cyber threat intelligence processes.

More information can be found here.

"},{"location":"deployment/managers/#history-manager","title":"History manager","text":"

This manager keeps tracks of user/connector interactions on entities in the platform.

It is designed to help users audit and understand the evolution of their CTI data.

"},{"location":"deployment/managers/#activity-manager","title":"Activity manager","text":"

The activity manager in OpenCTI is a component that monitors and logs the user actions in the platform such as login, settings update, and user activities if configured (read, udpate, etc.).

More information can be found here.

"},{"location":"deployment/managers/#background-task-manager","title":"Background task manager","text":"

Is a component that handles the execution of tasks, such as importing data, exporting data and mass operations.

More information can be found here.

"},{"location":"deployment/managers/#expiration-scheduler","title":"Expiration scheduler","text":"

The expiration scheduler is responsible for monitoring expired elements in the platform. It cancels the access rights of expired user accounts and revokes expired indicators from the platform.

"},{"location":"deployment/managers/#synchronization-manager","title":"Synchronization manager","text":"

The synchronization manager enables the data sharing between multiple OpenCTI platforms. It allows the user to create and configure synchronizers which are processes that connect to the live streams of remote OpenCTI platforms and import the data into the local platform.

"},{"location":"deployment/managers/#retention-manager","title":"Retention manager","text":"

The retention manager is a component that allows the user to define rules to help delete data in OpenCTI that is no longer relevant or useful. This helps to optimize the performance and storage of the OpenCTI platform and ensures the quality and accuracy of the data.

More information can be found here.

"},{"location":"deployment/managers/#notification-manager","title":"Notification manager","text":"

The notification manager is a component that allows the user to customize and receive alerts about events/changes in the platform.

More information can be found here.

"},{"location":"deployment/managers/#ingestion-manager","title":"Ingestion manager","text":"

The ingestion manager in OpenCTI is a component that manages the ingestion of data from RSS, TAXII and CSV feeds.

"},{"location":"deployment/managers/#playbook-manager","title":"Playbook manager","text":"

The playbook manager handles the automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform.

Please read the Playbook automation page to get more information.

"},{"location":"deployment/managers/#file-index-manager","title":"File index manager","text":"

The file indexing manager extracts and indexes the text content of the files, and stores it in the database. It allows users to search for text content within files uploaded to the platform.

More information can be found here.

"},{"location":"deployment/managers/#indicator-decay-manager","title":"Indicator decay manager","text":"

The indicator decay manager allows to update indicators score automatically based on configured decay rules.

More information can be found here.

"},{"location":"deployment/managers/#trash-manager","title":"Trash manager","text":"

The trash manager is responsible to delete permanently elements stored in the trash after a specified period of time (7 days by default).

"},{"location":"deployment/managers/#filigran-telemetry-manager","title":"Filigran telemetry manager","text":"

The telemetry manager collects periodically statistical data about platform usage.

More information about data telemetry can be found here.

"},{"location":"deployment/map/","title":"Deploy on-premise map server with OpenCTI styles","text":""},{"location":"deployment/map/#introduction","title":"Introduction","text":"

The OpenStreetMap tiles for the planet will take 80GB. Here are the instructions to deploy a local OpenStreetMap server with the OpenCTI styles.

"},{"location":"deployment/map/#create-directory-for-the-data-and-upload-planet-data","title":"Create directory for the data and upload planet data","text":"

When you will launch the map server container, it will be necessary to mount a volume with the planet tiles data. Just create the directory for the data.

mkdir /var/YOUR_DATA_DIR\n

We have hosted the free-to-use planet tiles, just download the planet data from filigran.io.

wget https://filigran.io/app/uploads/maptiler-osm-2020-02-10-v3.11-planet.mbtiles\n

Put the file maptiler-osm-2020-12-14-v3.11-planet.mbtiles in the data directory:

cp maptiler-osm-2020-12-14-v3.11-planet.mbtiles /var/YOUR_DATA_DIR/\n
"},{"location":"deployment/map/#start-docker","title":"Start Docker","text":"

Replace the port 80 by the port you would like to expose. Inside the Docker, the map server listen on the port 80.

docker run -d --restart always -v /var/YOUR_DATA_DIR:/data -p 80:80 klokantech/openmaptiles-server:latest \n
"},{"location":"deployment/map/#configure","title":"Configure","text":"

Download on your computer the OpenCTI JSON styles (you will have to upload it through the web UI later).

  • OpenCTI Map Dark mode style (filigran-dark2.json)
  • OpenCTI Map Light mode style (filigran-light2.json)

Now, you can access to the map server, you should see the following page:

On the next page, you should see the existing data:

On the next page, click on \"Advanced Options\":

Upload the filigran-dark2.json and filigran-light2.json files:

Save and run the server with default parameters:

"},{"location":"deployment/map/#opencti-parameters","title":"OpenCTI Parameters","text":"

Once the server is running, you should see the list of available styles:

Click on \"Viewer\", and take the URL:

\ud83d\udc49 http:/YOUR_URL/styles/{ID}/....

In the OpenCTI configuration, just put:

Parameter Environment variable Value Description app:map_tile_server_dark APP__MAP_TILE_SERVER_DARK http://{YOUR_MAP_SERVER}/styles/{ID_DARK}/{z}/{x}/{y}.png The address of the OpenStreetMap provider with dark theme style app:map_tile_server_light APP__MAP_TILE_SERVER_LIGHT http://{YOUR_MAP_SERVER}/styles/{ID_LIGHT}/{z}/{x}/{y}.png The address of the OpenStreetMap provider with light theme style

You're good to go!

"},{"location":"deployment/overview/","title":"Overview","text":"

Before starting the installation, let's discover how OpenCTI is working, which dependencies are needed and what are the minimal requirements to deploy it in production.

"},{"location":"deployment/overview/#architecture","title":"Architecture","text":"

The OpenCTI platform relies on several external databases and services in order to work.

"},{"location":"deployment/overview/#platform","title":"Platform","text":"

The platform is the central part of the OpenCTI technological stack. It allows users to access to the user interface but also provides the GraphQL API used by connectors and workers to insert data. In the context of a production deployment, you may need to scale horizontally and launch multiple platforms behind a load balancer connected to the same databases (ElasticSearch, Redis, S3, RabbitMQ).

"},{"location":"deployment/overview/#workers","title":"Workers","text":"

The workers are standalone Python processes consuming messages from the RabbitMQ broker in order to do asynchronous write queries. You can launch as many workers as you need to increase the write performances. At some point, the write performances will be limited by the throughput of the ElasticSearch database cluster.

Number of workers

If you need to increase performances, it is better to launch more platforms to handle worker queries. The recommended setup is to have at least one platform for 3 workers (ie. 9 workers distributed over 3 platforms).

"},{"location":"deployment/overview/#connectors","title":"Connectors","text":"

The connectors are third-party pieces of software (Python processes) that can play five different roles on the platform:

Type Description Examples EXTERNAL_IMPORT Pull data from remote sources, convert it to STIX2 and insert it on the OpenCTI platform. MITRE Datasets, MISP, CVE, AlienVault, Mandiant, etc. INTERNAL_ENRICHMENT Listen for new OpenCTI entities or users requests, pull data from remote sources to enrich. Shodan, DomainTools, IpInfo, etc. INTERNAL_IMPORT_FILE Extract data from files uploaded on OpenCTI through the UI or the API. STIX 2.1, PDF, Text, HTML, etc. INTERNAL_EXPORT_FILE Generate export from OpenCTI data, based on a single object or a list. STIX 2.1, CSV, PDF, etc. STREAM Consume a platform data stream and do something with events. Splunk, Elastic Security, Q-Radar, etc.

List of connectors

You can find all currently available connectors in the OpenCTI Ecosystem.

"},{"location":"deployment/overview/#infrastructure-requirements","title":"Infrastructure requirements","text":""},{"location":"deployment/overview/#dependencies","title":"Dependencies","text":"Component Version CPU RAM Disk type Disk space ElasticSearch / OpenSearch >= 8.0 / >= 2.9 2 cores \u2265 8GB SSD \u2265 16GB Redis >= 7.1 1 core \u2265 1GB SSD \u2265 16GB RabbitMQ >= 3.11 1 core \u2265 512MB Standard \u2265 2GB S3 / MinIO >= RELEASE.2023-02 1 core \u2265 128MB SSD \u2265 16GB"},{"location":"deployment/overview/#platform_1","title":"Platform","text":"Component CPU RAM Disk type Disk space OpenCTI Core 2 cores \u2265 8GB None (stateless) - Worker(s) 1 core \u2265 128MB None (stateless) - Connector(s) 1 core \u2265 128MB None (stateless) -

Clustering

To have more details about deploying OpenCTI and its dependencies in cluster mode, please read the dedicated section.

"},{"location":"deployment/resources/","title":"Other resources","text":""},{"location":"deployment/resources/#introduction","title":"Introduction","text":"

OpenCTI is an open and modular platform. A lot of connectors, plugins and clients are created by Filigran and by the community. You can find here other resources available to complete your OpenCTI journey.

"},{"location":"deployment/resources/#videos-training","title":"Videos & training","text":"
  • YouTube channel

    Watch demonstration videos, use case explanations, customers and community testimonies, and past webinars.

    Watch

  • Training courses

    Empower your journey with OpenCTI training courses for both analyst and administrators, and get your certificate.

    Learn

"},{"location":"deployment/resources/#articles-news","title":"Articles & news","text":"
  • Blog articles

    Read posts written by both Filigran teams and community members about OpenCTI features and use cases.

    Read

  • Newsletters

    Subscribe to Filigran newsletters to get informed about the latest evolutions of our product ecosystems.

    Subscribe

"},{"location":"deployment/resources/#analysis","title":"Analysis","text":"
  • Verticalized threat landscapes

    Access to monthly sectorial analysis from our experts team based on knowledge and data collected by our partners.

    Consult

  • Case studies

    Explore the Filigran case studies about stories and usages of the platform among our communities and customers.

    Download

"},{"location":"deployment/rollover/","title":"Indices and rollover policies","text":"

Default rollover policies

Since OpenCTI 5.9.0, rollover policies are automatically created when the platform is initialized for the first time. If your platform has been initialized using an older version of OpenCTI or if you would like to understand (and customize) rollover policies please read the following documentation.

"},{"location":"deployment/rollover/#introduction","title":"Introduction","text":"

ElasticSearch and OpenSearch both support rollover on indices. OpenCTI has been designed to be able to use aliases for indices and so supports index lifecycle policies very well. Thus, by default OpenCTI initializes indices with a suffix of -00001 and uses wildcards to query indices. When rollover policies are implemented (default starting OCTI 5.9.X if you initialized your platform at this version), indices are splitted to keep a reasonable volume of data in shards.

"},{"location":"deployment/rollover/#opencti-integration-user-permissions-in-opensearchelasticsearch","title":"OpenCTI Integration User Permissions in OpenSearch/ElasticSearch","text":"
  • Index Permissions

    • Patterns: opencti* (Dependent on the parameter elasticsearch:index_prefix value)
    • Permissions: indices_all
  • Cluster Permissions

    • cluster_composite_ops_ro
    • cluster_manage_index_templates
    • cluster:admin/ingest/pipeline/put
    • cluster:admin/opendistro/ism/policy/write
    • cluster:monitor/health
    • cluster:monitor/main
    • cluster:monitor/state
    • indices:admin/index_template/put
    • indices:data/read/scroll/clear
    • indices:data/read/scroll
    • indices:data/write/bulk

About indices:* in Cluster Permissions

It is crucial to include indices:* permissions in Cluster Permissions for the proper functioning of the OpenCTI integration. Removing these, even if already present in Index Permissions, may result in startup issues for the OpenCTI Platform.

"},{"location":"deployment/rollover/#elasticsearch-configuration","title":"ElasticSearch configuration","text":""},{"location":"deployment/rollover/#indices","title":"Indices","text":"

By default, a rollover policy is applied on all indices used by OpenCTI.

  • opencti_deleted_objects
  • opencti_files
  • opencti_history
  • opencti_inferred_entities
  • opencti_inferred_relationships
  • opencti_internal_objects
  • opencti_internal_relationships
  • opencti_stix_core_relationships
  • opencti_stix_cyber_observable_relationships
  • opencti_stix_cyber_observables
  • opencti_stix_domain_objects
  • opencti_stix_meta_objects
  • opencti_stix_meta_relationships
  • opencti_stix_sighting_relationships

For your information, the indices which can grow rapidly are:

  • Index opencti_stix_meta_relationships: it contains all the nested relationships between objects and labels / marking definitions / external references / authors, etc.
  • Index opencti_history: it contains the history log of all objects in the platform.
  • Index opencti_stix_cyber_observables: it contains all observables stored in the platform.
  • Index opencti_stix_core_relationships: it contains all main STIX relationships stored in the platform.
"},{"location":"deployment/rollover/#default-implemented-lifecycle-policy","title":"Default implemented lifecycle policy","text":"

Here is the recommended policy (initialized starting 5.9.X):

  • Maximum primary shard size: 50 GB
  • Maximum age: 365 days
  • Maximum documents: 75,000,000
"},{"location":"deployment/rollover/#applying-rollover-policies-on-existing-indices","title":"Applying rollover policies on existing indices","text":"

Procedure information

Please read the following only if your platform has been initialized before 5.9.0, otherwise lifecycle policies has been created (but you can still cutomize them).

Unfortunately, to be able to implement rollover policies on ElasticSearch / OpenSearch indices, it will be needed to re-index all the data in new indices using ElasticSearch capabilities.

"},{"location":"deployment/rollover/#shutdown","title":"Shutdown","text":"

First step is to shutdown your OpenCTI platform.

"},{"location":"deployment/rollover/#change-configuration","title":"Change configuration","text":"

Then, in the OpenCTI configuration, change the ElasticSearch / OpenSearch default prefix to octi (default is opencti).

"},{"location":"deployment/rollover/#create-the-rollover-policy","title":"Create the rollover policy","text":"

Create a rollover policy named octi-ilm-policy (in Kibana, Management > Index Lifecycle Policies):

  • Maximum primary shard size: 50 GB
  • Maximum age: 365 days
  • Maximum documents: 75,000,000

"},{"location":"deployment/rollover/#create-index-templates","title":"Create index templates","text":"

In Kibana, clone the opencti-index-template to have one index template by OpenCTI index with the appropriate rollover policy, index pattern and rollover alias (in Kibana, Management > Index Management > Index Templates).

Create the following index templates:

  • octi_deleted_objects
  • octi_files
  • octi_history
  • octi_inferred_entities
  • octi_inferred_relationships
  • octi_internal_objects
  • octi_internal_relationships
  • octi_stix_core_relationships
  • octi_stix_cyber_observable_relationships
  • octi_stix_cyber_observables
  • octi_stix_domain_objects
  • octi_stix_meta_objects
  • octi_stix_meta_relationships
  • octi_stix_sighting_relationships

Here is the overview of all templates (you should have something with octi_ instead of opencti_).

"},{"location":"deployment/rollover/#apply-rollover-policy-on-all-index-templates","title":"Apply rollover policy on all index templates","text":"

Then, going back in the index lifecycle policies screen, you can click on the \"+\" button of the octi-ilm-policy to Add the policy to index template, then add the policy to add previously created template with the proper \"Alias for rollover index\".

"},{"location":"deployment/rollover/#bootstrap-all-new-indices","title":"Bootstrap all new indices","text":"

Before we can re-index, we need to create the new indices with aliases.

PUT octi_history-000001\n{\n  \"aliases\": {\n    \"octi_history\": {\n      \"is_write_index\": true\n    }\n  }\n}\n

Repeat this step for all indices:

  • octi_deleted_objects
  • octi_files
  • octi_history
  • octi_inferred_entities
  • octi_inferred_relationships
  • octi_internal_objects
  • octi_internal_relationships
  • octi_stix_core_relationships
  • octi_stix_cyber_observable_relationships
  • octi_stix_cyber_observables
  • octi_stix_domain_objects
  • octi_stix_meta_objects
  • octi_stix_meta_relationships
"},{"location":"deployment/rollover/#re-index-all-indices","title":"Re-index all indices","text":"

Using the reindex API, re-index all indices one by one:

curl -X POST \"localhost:9200/_reindex?pretty\" -H 'Content-Type: application/json' -d'\n{\n  \"source\": {\n    \"index\": \"opencti_history-000001\"\n  },\n  \"dest\": {\n    \"index\": \"octi_history\"\n  }\n}\n'\n

You will see the rollover policy to be applied and the new indices are automatically rolled-over during re-indexation.

"},{"location":"deployment/rollover/#delete-all-old-indices","title":"Delete all old indices","text":"

Then just delete all indices with the prefix opencti_.

"},{"location":"deployment/rollover/#start-your-platform","title":"Start your platform","text":"

Start your platform, using the new indices.

Rollover documentation

To have more details about automatic rollover and lifecycle policies, please read the official ElasticSearch documentation.

"},{"location":"deployment/troubleshooting/","title":"Troubleshooting","text":"

This page aims to explain the typical errors you can have with your OpenCTI platform.

"},{"location":"deployment/troubleshooting/#finding-the-relevant-logs","title":"Finding the relevant logs","text":"

It is highly recommended to monitor the error logs of the platforms, workers and connectors. All the components have log outputs in an understandable JSON format. If necessary, it is always possible to increase the log level. In production, it is recommended to have the log level set to error.

"},{"location":"deployment/troubleshooting/#platform","title":"Platform","text":"

Here are some useful parameters for platform logging:

- APP__APP_LOGS__LOGS_LEVEL=[error|warning|info|debug]\n- APP__APP_LOGS__LOGS_CONSOLE=true # Output in the container console\n
"},{"location":"deployment/troubleshooting/#connectors","title":"Connectors","text":"

All connectors support the same set of parameters to manage the log level and outputs:

- OPENCTI_JSON_LOGGING=true # Enable / disable JSON logging\n- CONNECTOR_LOG_LEVEL=info=[error|warning|info|debug]\n
"},{"location":"deployment/troubleshooting/#workers","title":"Workers","text":"

The workers can have more or less verbose outputs:

- OPENCTI_JSON_LOGGING=true # Enable / disable JSON logging\n- WORKER_LOG_LEVEL=[error|warning|info|debug]\n
"},{"location":"deployment/troubleshooting/#elasticsearch-opensearch-data","title":"ElasticSearch / OpenSearch data","text":"

Kibana / OpenSearch dashboard

In case you need to troubleshoot the OpenCTI knowledge data, we recommend to install Kibana or OpenSearch dashboard.

"},{"location":"deployment/troubleshooting/#common-errors","title":"Common errors","text":""},{"location":"deployment/troubleshooting/#ingestion-technical-errors","title":"Ingestion technical errors","text":"

Missing reference to handle creation

After 5 retries, if an element required to create another element is missing, the platform raises an exception. It usually comes from a connector that generates inconsistent STIX 2.1 bundles.

Cant upsert entity. Too many entities resolved

OpenCTI received an entity which is matching too many other entities in the platform. In this condition we cannot take a decision. We need to dig into the data bundle to identify why it matches too much entities and fix the data in the bundle / or the platform according to what you expect.

Execution timeout, too many concurrent call on the same entities

The platform supports multi workers and multiple parallel creation but different parameters can lead to some locking timeout in the execution.

  • Throughput capacity of your ElasticSearch
  • Number of workers started at the same time
  • Dependencies between data
  • Merging capacity of OpenCTI

If you have this kind of error, limit the number of workers deployed. Try to find the right balance of the number of workers, connectors and elasticsearch sizing.

"},{"location":"deployment/troubleshooting/#ingestion-functional-errors","title":"Ingestion functional errors","text":"

Indicator of type yara is not correctly formatted

OpenCTI check the validity of the indicator rule.

Observable of type IPv4-Addr is not correctly formatted

OpenCTI check the validity of the observable value.

"},{"location":"deployment/troubleshooting/#dependencies-errors","title":"Dependencies errors","text":"

TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark...

Disk full, no space left on the device for ElasticSearch.

"},{"location":"deployment/upgrade/","title":"Upgrade","text":"

Depending on your installation mode, upgrade path may change.

Migrations

The platform is taking care of all necessary underlying migrations in the databases if any. You can upgrade OpenCTI from any version to the latest one, including skipping multiple major releases.

"},{"location":"deployment/upgrade/#using-docker","title":"Using Docker","text":"

Before applying this procedure, please update your docker-compose.yml file with the new version number of container images.

"},{"location":"deployment/upgrade/#for-single-node-docker","title":"For single node Docker","text":"
$ sudo docker-compose stop\n$ sudo docker-compose pull\n$ sudo docker-compose up -d\n
"},{"location":"deployment/upgrade/#for-docker-swarm","title":"For Docker swarm","text":"

For each of services, you have to run the following command:

$ sudo docker service update --force service_name\n
"},{"location":"deployment/upgrade/#manual-installation","title":"Manual installation","text":"

When upgrading the platform, you have to replace all files and restart the platform, the database migrations will be done automatically:

$ yarn serv\n
"},{"location":"development/api-usage/","title":"GraphQL playground","text":"

The GraphQL playground is an integrated development environment (IDE) provided by OpenCTI for exploring and testing GraphQL APIs. It offers a user-friendly interface that allows developers to interactively query the GraphQL schema, experiment with different queries, and visualize the responses.

"},{"location":"development/api-usage/#key-features","title":"Key features","text":""},{"location":"development/api-usage/#interactive-querying","title":"Interactive querying","text":"

The Playground provides a text editor where developers can write GraphQL queries, mutations, and subscriptions. As you type, the Playground offers syntax highlighting, autocompletion, and error checking to aid in query composition.

"},{"location":"development/api-usage/#documentation","title":"Documentation","text":"

Developers can access comprehensive documentation for the GraphQL schema directly within the Playground. This documentation includes descriptions of all available types, fields, and directives, making it easy to understand the data model and construct queries.

"},{"location":"development/api-usage/#query-history","title":"Query history","text":"

The playground keeps track of previously executed queries, allowing developers to revisit and reuse queries from previous sessions. This feature streamlines the development process by eliminating the need to retype complex queries.

"},{"location":"development/api-usage/#response-visualization","title":"Response visualization","text":"

Upon executing a query, the playground displays the response data in a structured and readable format. JSON responses are presented in a collapsible tree view, making it easy to navigate nested data structures and inspect individual fields.

"},{"location":"development/api-usage/#schema-exploration","title":"Schema exploration","text":"

Developers can explore the GraphQL schema using the built-in schema viewer. This feature provides a graphical representation of the schema, showing types, fields, and their relationships. Developers can explore the schema and understand its structure.

"},{"location":"development/api-usage/#getting-started","title":"Getting started","text":"

To access the GraphQL playground, navigate to the GraphQL endpoint of your OpenCTI instance: https://[your-opencti-instance]/graphql. Then, follow these steps to utilize the playground:

  1. Query editor: Write GraphQL queries, mutations, and subscriptions in the text editor. Use syntax highlighting and autocompletion to speed up query composition.
  2. Documentation explorer: Access documentation for the GraphQL schema by clicking on the \"Docs\" tab on the right. Browse types, fields, and descriptions to understand the available data and query syntax.
  3. Query history: View and execute previously executed queries from the \"History\" tab on the top. Reuse queries and experiment with variations without retyping.
  4. Response pane: Visualize query responses in the response pane. Expand and collapse sections to navigate complex data structures and inspect individual fields.
  5. Schema viewer: Explore the GraphQL schema interactively using the \"Schema\" tab on the right. Navigate types, fields, and relationships to understand the data model and plan queries.
"},{"location":"development/api-usage/#external-resources","title":"External Resources","text":"

For a more in-depth understanding of GraphQL and its usage, consider exploring the following external resources:

  • GraphQL Official Documentation
  • GraphQL query documentation
  • How to GraphQL
"},{"location":"development/connectors/","title":"Connector development","text":""},{"location":"development/connectors/#introduction","title":"Introduction","text":"

A connector in OpenCTI is a service that runs next to the platform and can be implemented in almost any programming language that has STIX2 support. Connectors are used to extend the functionality of OpenCTI and allow operators to shift some of the processing workload to external services. To use the conveniently provided OpenCTI connector SDK you need to use Python3 at the moment.

We choose to have a very decentralized approach on connectors, in order to bring a maximum freedom to developers and vendors. So a connector on OpenCTI can be defined by a standalone Python 3 process that pushes an understandable format of data to an ingestion queue of messages.

Each connector must implement a long-running process that can be launched just by executing the main Python file. The only mandatory dependency is the OpenCTIConnectorHelper class that enables the connector to send data to OpenCTI.

"},{"location":"development/connectors/#getting-started","title":"Getting started","text":"

In the beginning first think about your use-case to choose an appropriate connector type - what do want to achieve with your connector? The following table gives you an overview of the current connector types and some typical use-cases:

Connector types

Type Typical use cases Example connector EXTERNAL_IMPORT Integrate external TI provider, Integrate external TI platform AlienVault INTERNAL_ENRICHMENT Enhance existing data with additional knowledge AbuseIP INTERNAL_IMPORT_FILE (Bulk) import knowledge from files Import document INTERNAL_EXPORT_FILE (Bulk) export knowledge to files STIX 2.1, CSV. STREAM Integrate external TI provider, Integrate external TI platform Elastic Security

After you've selected your connector type make yourself familiar with STIX2 and the supported relationships in OpenCTI. Having some knowledge about the internal data models with help you a lot with the implementation of your idea.

"},{"location":"development/connectors/#preparation","title":"Preparation","text":""},{"location":"development/connectors/#environment-setup","title":"Environment Setup","text":"

To develop and test your connector, you need a running OpenCTI instance with the frontend and the messaging broker accessible. If you don't plan on developing anything for the OpenCTI platform or the frontend, the easiest setup for the connector development is using the docker setup, For more details see here.

"},{"location":"development/connectors/#coding-setup","title":"Coding Setup","text":"

To give you an easy starting point we prepared an example connector in the public repository you can use as template to bootstrap your development.

Some prerequisites we recommend to follow this tutorial:

  • Code editor with good Python3 support (e.g. Visual Studio Code with the Python extension pack)
  • Python3 + setuptools is installed and configured
  • Command shell (either Linux/Mac terminal or WSL on Windows)

In the terminal check out the connectors repository and copy the template connector to $myconnector (replace it with your name throughout the following text examples).

$ pip3 install black flake8 pycti\n# Fork the current repository, then clone your fork\n$ git clone https://github.com/YOUR-USERNAME/connectors.git\n$ cd connectors\n$ git remote add upstream https://github.com/OpenCTI-Platform/connectors.git\n# Create a branch for your feature/fix\n$ git checkout -b [branch-name]\n# Copy the appropriate template directory for the connector type\n$ cp -r templates/$connector_type $connector_type/$myconnector\n$ cd $connector_type/$myconnector\n$ ls -R\nDockerfile              docker-compose.yml      requirements.txt\nREADME.md               entrypoint.sh           src\n\n./src:\nlib     main.py\n\n./src/lib:\n$connector_type.py\n
"},{"location":"development/connectors/#changing-the-template","title":"Changing the template","text":"

There are a few files in the template we need to change for our connector to be unique. You can check for all places you need to change you connector name with the following command (the output will look similar):

$ grep -Ri template .\n\nREADME.md:# OpenCTI Template Connector\nREADME.md:| `connector_type`                     | `CONNECTOR_TYPE`                    | Yes          | Must be `Template_Type` (this is the connector type).                                                                                                      |\nREADME.md:| `connector_name`                     | `CONNECTOR_NAME`                    | Yes          | Option `Template`                                                                                                                                          |\nREADME.md:| `connector_scope`                    | `CONNECTOR_SCOPE`                   | Yes          | Supported scope: Template Scope (MIME Type or Stix Object)                                                                                                 |\nREADME.md:| `template_attribute`                 | `TEMPLATE_ATTRIBUTE`                | Yes          | Additional setting for the connector itself                                                                                                                |\ndocker-compose.yml:  connector-template:\ndocker-compose.yml:    image: opencti/connector-template:4.5.5\ndocker-compose.yml:      - CONNECTOR_TYPE=Template_Type\ndocker-compose.yml:      - CONNECTOR_NAME=Template\ndocker-compose.yml:      - CONNECTOR_SCOPE=Template_Scope # MIME type or Stix Object\nentrypoint.sh:cd /opt/opencti-connector-template\nDockerfile:COPY src /opt/opencti-template\nDockerfile:    cd /opt/opencti-connector-template && \\\nsrc/main.py:class Template:\nsrc/main.py:            \"TEMPLATE_ATTRIBUTE\", [\"template\", \"attribute\"], config, True\nsrc/main.py:        connectorTemplate = Template()\nsrc/main.py:        connectorTemplate.run()\nsrc/config.yml.sample:  type: 'Template_Type'\nsrc/config.yml.sample:  name: 'Template'\nsrc/config.yml.sample:  scope: 'Template_Scope' # MIME type or SCO\n

Required changes:

  • Change Template or templatementions to your connector name e.g. ImportCsv or importcsv
  • Change TEMPLATE mentions to your connector name e.g. IMPORTCSV
  • Change Template_Scope mentions to the required scope of your connector. For processing imported files, that can be the Mime type e.g. application/pdf or for enriching existing information in OpenCTI, define the STIX object's name e.g. Report. Multiple scopes can be separated by a simple ,
  • Change Template_Type to the connector type you wish to develop. The OpenCTI types are defined hereafter:
    • EXTERNAL_IMPORT
    • INTERNAL_ENRICHMENT
    • INTERNAL_EXPORT_FILE
    • INTERNAL_IMPORT_FILE
    • STREAM
"},{"location":"development/connectors/#development","title":"Development","text":""},{"location":"development/connectors/#initialize-the-opencti-connector-helper","title":"Initialize the OpenCTI connector helper","text":"

After getting the configuration parameters of your connector, you have to initialize the OpenCTI connector helper by using the pycti Python library. This is shown in the following example:

class TemplateConnector:\n    def __init__(self):\n                # Instantiate the connector helper from config\n        config_file_path = os.path.dirname(os.path.abspath(__file__)) + \"/config.yml\"\n        config = (\n            yaml.load(open(config_file_path), Loader=yaml.SafeLoader)\n            if os.path.isfile(config_file_path)\n            else {}\n        )\n        self.helper = OpenCTIConnectorHelper(config)\n                self.custom_attribute = get_config_variable(\n            \"TEMPLATE_ATTRIBUTE\", [\"template\", \"attribute\"], config\n        )\n

Since there are some basic differences in the tasks of the different connector classes, the structure is also a bit class dependent. While the external-import and the stream connector run independently in a regular interval or constantly, the other 3 connector classes only run when being requested by the OpenCTI platform.

The self-triggered connectors run independently, but the OpenCTI need to define a callback function, which can be executed for the connector to start its work. This is done via self.helper.listen(self._process_message). In the appended examples, the difference of the setup can be seen.

Self-triggered Connectors

  • external-import
  • stream

OpenCTI triggered

  • internal-enrichment
  • internal-import
  • internal-export
from pycti import OpenCTIConnectorHelper, get_config_variable\n\nclass TemplateConnector:\n    def __init__(self) -> None:\n                # Initialization procedures\n                [...]\n        self.template_interval = get_config_variable(\n            \"TEMPLATE_INTERVAL\", [\"template\", \"interval\"], config, True\n        )\n\n    def get_interval(self) -> int:\n        return int(self.template_interval) * 60 * 60 * 24\n\n    def run(self) -> None:\n                # Main procedure\n\nif __name__ == \"__main__\":\n    try:\n        template_connector = TemplateConnector()\n        template_connector.run()\n    except Exception as e:\n        print(e)\n        time.sleep(10)\n        exit(0)\n
from pycti import OpenCTIConnectorHelper, get_config_variable\n\nclass TemplateConnector:\n    def __init__(self) -> None:\n                # Initialization procedures\n                [...]\n\n    def _process_message(self, data: dict) -> str:\n                # Main procedure                \n\n    # Start the main loop\n    def start(self) -> None:\n        self.helper.listen(self._process_message)\n\nif __name__ == \"__main__\":\n    try:\n        template_connector = TemplateConnector()\n        template_connector.start()\n    except Exception as e:\n        print(e)\n        time.sleep(10)\n        exit(0)\n
"},{"location":"development/connectors/#write-and-read-operations","title":"Write and Read Operations","text":"

When using the OpenCTIConnectorHelper class, there are two way for reading from or writing data to the OpenCTI platform.

  1. via the OpenCTI API interface via self.helper.api
  2. via the OpenCTI worker via self.send_stix2_bundle
"},{"location":"development/connectors/#sending-data-to-the-opencti-platform","title":"Sending data to the OpenCTI platform","text":"

The recommended way for creating or updating data in the OpenCTI platform is via the OpenCTI worker. This enables the connector to just send and forget about thousands of entities at once to without having to think about the ingestion order, performance or error handling.

\u26a0\ufe0f **Please DO NOT use the api interface to create new objects in connectors.**

The OpenCTI connector helper method send_stix2_bundle must be used to send data to OpenCTI. The send_stix2_bundle function takes 2 arguments.

  1. A serialized STIX2 bundle as a string (mandatory)
  2. A list of entities types that should be ingested (optional)

Here is an example using the STIX2 Python library:

from stix2 import Bundle, AttackPattern\n\n[...]\n\nattack_pattern = AttackPattern(name='Evil Pattern')\n\nbundle_objects = []\nbundle_objects.append(attack_pattern)\n\nbundle = Bundle(objects=bundle_objects).serialize()\nbundles_sent = self.opencti_connector_helper.send_stix2_bundle(bundle)\n
"},{"location":"development/connectors/#reading-from-the-opencti-platform","title":"Reading from the OpenCTI platform","text":"

Read queries to the OpenCTI platform can be achieved using the API and the STIX IDs can be attached to reports to create the relationship between those two entities.

entity = self.helper.api.vulnerability.read(\n    filters={\"key\": \"name\", \"values\": [\"T1234\"]}\n)\n

If you want to add the found entity via objects_refs to another SDO, simply add a list of stix_ids to the SDO. Here's an example using the entity from the code snippet above:

from stix2 import Report\n\n[...]\n\nreport = Report(\n    id=report[\"standard_id\"],\n  object_refs=[entity[\"standard_id\"]],\n)\n
"},{"location":"development/connectors/#logging","title":"Logging","text":"

When something crashes for a user, you as a developer want to know as much as possible about this incident to easily improve your code and remove this issue. To do so, it is very helpful if your connector documents what it does. Use info messages for big changes like the beginning or the finishing of an operation, but to facilitate your bug removal attempts, implement debug messages for minor operation changes to document different steps in your code.

When encountering a crash, the connector's user can easily restart the troubling connector with the debug logging activated.

  • CONNECTOR_LOG_LEVEL=debug

Using those additional log messages, the bug report is more enriched with information about the possible cause of the problem. Here's an example of how the logging should be implemented:

        def run(self) -> None:\n                self.helper.log_info('Template connector starts')\n                results = self._ask_for_news()\n                [...]\n\n        def _ask_for_news() -> None:\n                overall = []\n                for i in range(0, 10):\n                        self.log_debug(f\"Asking about news with count '{i}'\")\n                        # Do something\n                        self.log_debug(f\"Resut: '{result}'\")\n                        overall.append(result)\n                return overall\n

Please make sure that the debug messages rich of useful information, but that they are not redundant and that the user is not drowned by unnecessary information.

"},{"location":"development/connectors/#additional-implementations","title":"Additional implementations","text":"

If you are still unsure about how to implement certain things in your connector, we advise you to have a look at the code of other connectors of the same type. Maybe they are already using an approach which is suitable for addressing to your problem.

"},{"location":"development/connectors/#opencti-triggered-connector-special-cases","title":"OpenCTI triggered Connector - Special cases","text":""},{"location":"development/connectors/#data-layout-of-dictionary-from-callback-function","title":"Data Layout of Dictionary from Callback function","text":"

OpenCTI sends the connector a few instructions via the data dictionary in the callback function. Depending on the connector type, the data dictionary content is a bit different. Here are a few examples for each connector type.

Internal Import Connector

{ \n  \"file_id\": \"<fileId>\",\n  \"file_mime\": \"application/pdf\", \n  \"file_fetch\": \"storage/get/<file_id>\", // Path to get the file\n  \"entity_id\": \"report--82843863-6301-59da-b783-fe98249b464e\", // Context of the upload\n}\n

Internal Enrichment Connector

{ \n  \"entity_id\": \"<stixCoreObjectId>\" // StixID of the object wanting to be enriched\n}\n

Internal Export Connector

{ \n  \"export_scope\": \"single\", // 'single' or 'list'\n  \"export_type\": \"simple\", // 'simple' or 'full'\n  \"file_name\": \"<fileName>\", // Export expected file name\n  \"max_marking\": \"<maxMarkingId>\", // Max marking id\n  \"entity_type\": \"AttackPattern\", // Exported entity type\n  // ONLY for single entity export\n  \"entity_id\": \"<entity.id>\", // Exported element\n  // ONLY for list entity export\n  \"list_params\": \"[<parameters>]\" // Parameters for finding entities\n}\n
"},{"location":"development/connectors/#self-triggered-connector-special-cases","title":"Self triggered Connector - Special cases","text":""},{"location":"development/connectors/#initiating-a-work-before-pushing-data","title":"Initiating a 'Work' before pushing data","text":"

For self-triggered connectors, OpenCTI has to be told about new jobs to process and to import. This is done by registering a so called work before sending the stix bundle and signalling the end of a work. Here an example:

By implementing the work registration, they will show up as shown in this screenshot for the MITRE ATT&CK connector:

def run() -> None:\n        # Anounce upcoming work\n        timestamp = int(time.time())\n        now = datetime.utcfromtimestamp(timestamp)\n    friendly_name = \"Template run @ \" + now.strftime(\"%Y-%m-%d %H:%M:%S\")\n    work_id = self.helper.api.work.initiate_work(\n                self.helper.connect_id, friendly_name\n        )\n\n        [...]\n        # Send Stix bundle\n        self.helper.send_stix2_bundle(\n                bundle,\n                entities_types=self.helper.connect_scope,\n                update=True,\n                work_id=work_id,\n        )\n        # Finish the work\n        self.helper.log_info(\n            f\"Connector successfully run, storing last_run as {str(timestamp)}\"\n    )              \n        message = \"Last_run stored, next run in: {str(round(self.get_interval() / 60 / 60 / 24, 2))} days\"\n        self.helper.api.work.to_processed(work_id, message)\n
"},{"location":"development/connectors/#interval-handling","title":"Interval handling","text":"

The connector is also responsible for making sure that it runs in certain intervals. In most cases, the intervals are definable in the connector config and then only need to be set and updated during the runtime.

class TemplateConnector:\n    def __init__(self) -> None:\n                # Initialization procedures\n                [...]\n        self.template_interval = get_config_variable(\n            \"TEMPLATE_INTERVAL\", [\"template\", \"interval\"], config, True\n        )\n\n    def get_interval(self) -> int:\n        return int(self.template_interval) * 60 * 60 * 24\n\n        def run(self) -> None:\n        self.helper.log_info(\"Fetching knowledge...\")\n        while True:\n            try:\n                # Get the current timestamp and check\n                timestamp = int(time.time())\n                current_state = self.helper.get_state()\n                if current_state is not None and \"last_run\" in current_state:\n                    last_run = current_state[\"last_run\"]\n                    self.helper.log_info(\n                        \"Connector last run: \"\n                        + datetime.utcfromtimestamp(last_run).strftime(\n                            \"%Y-%m-%d %H:%M:%S\"\n                        )\n                    )\n                else:\n                    last_run = None\n                    self.helper.log_info(\"Connector has never run\")\n                # If the last_run is more than interval-1 day\n                if last_run is None or (\n                    (timestamp - last_run)\n                    > ((int(self.template_interval) - 1) * 60 * 60 * 24)\n                ):\n                    timestamp = int(time.time())\n                    now = datetime.utcfromtimestamp(timestamp)\n                    friendly_name = \"Connector run @ \" + now.strftime(\"%Y-%m-%d %H:%M:%S\")\n\n                                        ###\n                                        # RUN CODE HERE     \n                                        ###\n\n                    # Store the current timestamp as a last run\n                    self.helper.log_info(\n                        \"Connector successfully run, storing last_run as \"\n                        + str(timestamp)\n                    )\n                    self.helper.set_state({\"last_run\": timestamp})\n                    message = (\n                        \"Last_run stored, next run in: \"\n                        + str(round(self.get_interval() / 60 / 60 / 24, 2))\n                        + \" days\"\n                    )\n                    self.helper.api.work.to_processed(work_id, message)\n                    self.helper.log_info(message)\n                    time.sleep(60)\n                else:\n                    new_interval = self.get_interval() - (timestamp - last_run)\n                    self.helper.log_info(\n                        \"Connector will not run, next run in: \"\n                        + str(round(new_interval / 60 / 60 / 24, 2))\n                        + \" days\"\n                    )\n                    time.sleep(60)\n
"},{"location":"development/connectors/#running-the-connector","title":"Running the connector","text":"

For development purposes, it is easier to simply run the python script locally until everything works as it should.

$ virtualenv env\n$ source ./env/bin/activate\n$ pip3 install -r requirements\n$ cp config.yml.sample config.yml\n# Define the opencti url and token, as well as the connector's id\n$ vim config.yml\n$ python3 main.py\nINFO:root:Listing Threat-Actors with filters null.\nINFO:root:Connector registered with ID: a2de809c-fbb9-491d-90c0-96c7d1766000\nINFO:root:Starting ping alive thread\n...\n
"},{"location":"development/connectors/#final-testing","title":"Final Testing","text":"

Before submitting a Pull Request, please test your code for different use cases and scenarios. We don't have an automatic testing suite for the connectors yet, thus we highly depend on developers thinking about creative scenarios their code could encounter.

"},{"location":"development/connectors/#prepare-for-release","title":"Prepare for release","text":"

If you plan to provide your connector to be used by the community (\u2764\ufe0f) your code should pass the following (minimum) criteria.

# Linting with flake8 contains no errors or warnings\n$ flake8 --ignore=E,W\n# Verify formatting with black\n$ black .\nAll done! \u2728 \ud83c\udf70 \u2728\n1 file left unchanged.\n# Verify import sorting\n$ isort --profile black .\nFixing /path/to/connector/file.py\n# Push you feature/fix on Github\n$ git add [file(s)]\n$ git commit -m \"[connector_name] descriptive message\"\n$ git push origin [branch-name]\n# Open a pull request with the title \"[connector_name] message\"\n

If you have any trouble with this just reach out to the OpenCTI core team. We are happy to assist with this.

"},{"location":"development/environment_ubuntu/","title":"Prerequisites Ubuntu","text":"

Development stack require some base software that need to be installed.

"},{"location":"development/environment_ubuntu/#docker-or-podman","title":"Docker or podman","text":"

Platform dependencies in development are deployed through container management, so you need to install a container stack.

We currently support docker and podman.

$ sudo apt-get install docker docker-compose curl\n
sudo apt-get -y install podman\n

As OpenCTI has a dependency to ElasticSearch, you have to set the vm.max_map_count before running the containers, as mentioned in the ElasticSearch documentation.

$ sudo sysctl -w vm.max_map_count=262144\n
"},{"location":"development/environment_ubuntu/#nodejs-and-yarn","title":"NodeJS and yarn","text":"

The platform is developed on nodejs technology, so you need to install node and the yarn package manager.

$ sudo apt-get install nodejs\n$ sudo curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -\n$ sudo echo \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list\n$ sudo apt-get update && sudo apt-get install yarn\n
"},{"location":"development/environment_ubuntu/#python-runtime","title":"Python runtime","text":"

For worker and connectors, a python runtime is needed.

$ sudo apt-get install python3 python3-pip\n
"},{"location":"development/environment_ubuntu/#git-and-dev-tool","title":"Git and dev tool","text":"
  • Install Git from apt
$ sudo apt-get install git-all\n
  • Install your preferred IDE
    • Intellij community edition - https://www.jetbrains.com/idea/download/
    • VSCode - https://code.visualstudio.com/
"},{"location":"development/environment_windows/","title":"Prerequisites Windows","text":"

Development stack require some base software that need to be installed.

"},{"location":"development/environment_windows/#docker-or-podman","title":"Docker or podman","text":"

Platform dependencies in development are deployed through container management, so you need to install a container stack.

We currently support docker and postman.

Docker Desktop from - https://docs.docker.com/desktop/install/windows-install/

  • Install new version of - https://docs.microsoft.com/windows/wsl/wsl2-kernel. This will require a reboot.
  • Shell out to CMD as Administrator and run the following powershell command:

wsl --set-default-version 2

  • Reboot computer and continue to next step
  • Load Docker Application
  • NOTE DOCKER LICENSE - You are agreeing to the licence for Non-commercial Open Source Project use. OpenCTI is Open Source and the version you would be possibly contributing to enhancing is the unpaid non-commercial/non-enterprise version. If you intention is different - please consult with your organization's legal/licensing department.
  • Leave Docker Desktop running
"},{"location":"development/environment_windows/#nodejs-and-yarn","title":"NodeJS and yarn","text":"

The platform is developed on nodejs technology, so you need to install node and the yarn package manager.

  • Install NodeJS from - https://nodejs.org/download/release/v16.20.0/node-v16.20.0-x64.msi
  • Select the option for installing Chocolatey on the Tools for Native Modules screen
    • Will do this install for you automatically - https://chocolatey.org/packages/visualstudio2019-workload-vctools
    • Includes Python 3.11.4
  • Shell out to CMD prompt as Administrator and install/run:

    • pip3 install pywin32
  • Configure Yarn (https://yarnpkg.com/getting-started/install)

  • Open CMD as Administrator and run the following command:
    • corepack enable
"},{"location":"development/environment_windows/#python-runtime","title":"Python runtime","text":"

For worker and connectors, a python runtime is needed. Even if you already have a python runtime installed through node installation, on windows some nodejs package will be recompiled with python and C++ runtime.

For this reason Visual Studio Build Tools is required.

  • Install Visual Studio Build Tools from - https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools
  • Check off Desktop Development with C++
  • Run install
"},{"location":"development/environment_windows/#git-and-dev-tool","title":"Git and dev tool","text":"
  • Download GIT for Windows (64-bit Setup)- https://git-scm.com/download/win
  • Just use defaults on each screen

  • Install your preferred IDE

  • Intellij community edition - https://www.jetbrains.com/idea/download/
  • VSCode - https://code.visualstudio.com/docs/?dv=win64
"},{"location":"development/platform/","title":"Platform development","text":""},{"location":"development/platform/#introduction","title":"Introduction","text":"

This summary should give you a detailed setup description for initiating the OpenCTI setup environment necessary for developing on the OpenCTI platform, a client library or the connectors. This page document how to set up an \"All-in-One\" development environment for OpenCTI. The devenv will contain data of 3 different repositories:

  • Platform: https://github.com/OpenCTI-Platform/opencti
  • Connectors: https://github.com/OpenCTI-Platform/connectors
  • Client python: https://github.com/OpenCTI-Platform/client-python
"},{"location":"development/platform/#platform","title":"Platform","text":"

Contains the platform OpenCTI project code base:

  • docker-compose (docker or podman) ~/opencti/opencti-platform/opencti-dev
  • Web frontend (nodejs / react) ~/opencti/opencti-platform/opencti-graphql
  • Backend (nodejs) ~/opencti/opencti-platform/opencti-frontend
  • Worker (nodejs / python) ~/opencti/opencti-worker
"},{"location":"development/platform/#connectors","title":"Connectors","text":"

Contains a lot of developed connectors, as a source of inspiration for your new connector.

"},{"location":"development/platform/#client-python","title":"Client python","text":"

Contains the source code of the python library used in worker or connectors.

"},{"location":"development/platform/#prerequisites","title":"Prerequisites","text":"

Some tools are needed before starting to develop. Please check Ubuntu prerequisites or Windows prerequisites

"},{"location":"development/platform/#clone-the-projects","title":"Clone the projects","text":"

Fork and clone the git repositories

  • https://github.com/OpenCTI-Platform/opencti/ - frontend / backend
  • https://github.com/OpenCTI-Platform/connectors - connectors
  • https://github.com/OpenCTI-Platform/docker - docker stack
  • https://github.com/OpenCTI-Platform/client-python/ - python client
"},{"location":"development/platform/#dependencies-containers","title":"Dependencies containers","text":"

In development dependencies are deployed trough containers. A development compose file is available in ~/opencti/opencti-platform/opencti-dev

cd ~/docker\n#Start the stack in background\ndocker-compose -f ./docker-compose-dev.yml up -d\n

You now have all the dependencies of OpenCTI running and waiting for product to run.

"},{"location":"development/platform/#backend-api","title":"Backend / API","text":""},{"location":"development/platform/#python-virtual-env","title":"Python virtual env","text":"

The GraphQL API is developed in JS and with some python code. As it's an \"all-in-one\" installation, the python environment will be installed in a virtual environment.

cd ~/opencti/opencti-platform/opencti-graphql\npython3 -m venv .venv --prompt \"graphql\"\nsource .venv/bin/activate\npip install --upgrade pip wheel setuptools\nyarn install\nyarn install:python \ndeactivate\n
"},{"location":"development/platform/#development-configuration","title":"Development configuration","text":"

The API can be specifically configured with files depending on the starting profile. By default, the default.json file is used and will be correctly configured for local usage except for admin password.

So you need to create a development profile file. You can duplicate the default file and adapt if you need.

cd ~/opencti/opencti-platform/opencti-graphql/config\ncp default.json development.json\n

At minimum adapt the admin part for the password and token.

    \"admin\": {\n      \"email\": \"admin@opencti.io\",\n      \"password\": \"MyNewPassord\",\n      \"token\": \"UUID generated with https://www.uuidgenerator.net\"\n    }\n

"},{"location":"development/platform/#install-start","title":"Install / start","text":"

Before starting the backend you need to install the nodejs modules

cd ~/opencti/opencti-platform/opencti-graphql\nyarn install\n

Then you can simply start the backend API with the yarn start command

cd ~/opencti/opencti-platform/opencti-graphql\nyarn start\n

The platform will start logging some interesting information

{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[OPENCTI] Starting platform\",\"timestamp\":\"2023-07-02T16:37:10.984Z\",\"version\":\"5.8.7\"}\n{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[OPENCTI] Checking dependencies statuses\",\"timestamp\":\"2023-07-02T16:37:10.987Z\",\"version\":\"5.8.7\"}\n{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[SEARCH] Elasticsearch (8.5.2) client selected / runtime sorting enabled\",\"timestamp\":\"2023-07-02T16:37:11.014Z\",\"version\":\"5.8.7\"}\n{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[CHECK] Search engine is alive\",\"timestamp\":\"2023-07-02T16:37:11.015Z\",\"version\":\"5.8.7\"}\n...\n{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[INIT] Platform initialization done\",\"timestamp\":\"2023-07-02T16:37:11.622Z\",\"version\":\"5.8.7\"}\n{\"category\":\"APP\",\"level\":\"info\",\"message\":\"[OPENCTI] API ready on port 4000\",\"timestamp\":\"2023-07-02T16:37:12.382Z\",\"version\":\"5.8.7\"}\n

If you want to start on another profile you can use the -e parameter. For example here to use the profile.json configuration file.

yarn start -e profile\n
"},{"location":"development/platform/#code-check","title":"Code check","text":"

Before pushing your code you need to validate the syntax and ensure the testing will be validated.

"},{"location":"development/platform/#for-validation","title":"For validation","text":"

yarn lint

yarn check-ts

"},{"location":"development/platform/#for-testing","title":"For testing","text":"

For starting the test you will need to create a test.json configuration file. You can use the same dependencies by only adapting all prefix for all dependencies.

Tests are using dedicated indices in the Elastic database (prefixed with test-* or the prefix that you have set up in test.json).

The following command will run the complete test suite using vitest, which might take more than 30 minutes. It starts by cleaning up the test database and seeding a minimal dataset. The file vitest.config.test.ts can be edited to run only a specific file pattern.

yarn test:dev

We also provide utility scripts to ease the development of new tests, especially integration tests that rely on the sample data loaded after executing 00-inject/loader-test.ts.

To solely initialize the test database with this sample dataset run:

yarn test:dev:init

And then, execute the following command to run the pattern specified in the file vitest.config.test.ts, or add a file name to the command line to run only this test file.

yarn test:dev:resume

This last command will NOT cleanup & initialize the test database and thus will be quicker to execute.

"},{"location":"development/platform/#frontend","title":"Frontend","text":""},{"location":"development/platform/#install-start_1","title":"Install / start","text":"

Before starting the backend you need to install the nodejs modules

cd ~/opencti/opencti-platform/opencti-front\nyarn install\n

Then you can simply start the frontend with the yarn start command

cd ~/opencti/opencti-platform/opencti-front\nyarn start\n

The frontend will start with some interesting information

[INFO] [default] compiling...\n[INFO] [default] compiled documents: 1592 reader, 1072 normalization, 1596 operation text\n[INFO] Compilation completed.\n[INFO] Done.\n[HPM] Proxy created: /stream  -> http://localhost:4000\n[HPM] Proxy created: /storage  -> http://localhost:4000\n[HPM] Proxy created: /taxii2  -> http://localhost:4000\n[HPM] Proxy created: /feeds  -> http://localhost:4000\n[HPM] Proxy created: /graphql  -> http://localhost:4000\n[HPM] Proxy created: /auth/**  -> http://localhost:4000\n[HPM] Proxy created: /static/flags/**  -> http://localhost:4000\n

The web UI should be accessible on http://127.0.0.1:3000

"},{"location":"development/platform/#code-check_1","title":"Code check","text":"

Before pushing your code you need to validate the syntax and ensure the testing will be validated.

"},{"location":"development/platform/#for-validation_1","title":"For validation","text":"

yarn lint

yarn check-ts

"},{"location":"development/platform/#for-testing_1","title":"For testing","text":"

yarn test

"},{"location":"development/platform/#worker","title":"Worker","text":"

Running a worker is required when you want to develop on the ingestion or import/export connectors.

"},{"location":"development/platform/#python-virtual-env_1","title":"Python virtual env","text":"
cd ~/opencti/opencti-worker/src\npython3 -m venv .venv --prompt \"worker\"\nsource .venv/bin/activate\npip3 install --upgrade pip wheel setuptools\npip3 install -r requirements.txt\ndeactivate\n
"},{"location":"development/platform/#install-start_2","title":"Install / start","text":"
cd ~/opencti/opencti-worker/src\nsource .venv/bin/activate\npython worker.py\n
"},{"location":"development/platform/#connectors_1","title":"Connectors","text":"

For connectors development, please take a look to Connectors development dedicated page.

"},{"location":"development/platform/#production-build","title":"Production build","text":"

Based on development source you can build the package for production. This package will be minified and optimized with esbuild.

$ cd opencti-frontend\n$ yarn build\n$ cd ../opencti-graphql\n$ yarn build\n

After the build you can start the production build with yarn serv. This build will use the production.json configuration file.

$ cd ../opencti-graphql\n$ yarn serv\n
"},{"location":"development/platform/#continuous-integration-and-features-cross-repository","title":"Continuous Integration and features cross repository","text":"

When a feature requires changes in two or more repositories in opencti, connectors and client-python; then some specific convention must be used to have the continuous integration build them all together.

"},{"location":"development/platform/#naming-convention-of-branch","title":"Naming convention of branch","text":"

The Pull Request on opencti repository should be (issue or bug)/number + optional, example: issue/7062-contributing

The pull request on connector or client-python should refer to the opencti one by starting with \"opencti/\" and then the same name. Example: opencti/issue/7062-contributing

Note that if there are several matches, the first one is taken. So for example having issue/7062-contributing and issue/7062 that are both marked as \"multi-repository\" is not a good idea.

"},{"location":"development/platform/#labels","title":"Labels","text":"

All Pull Requests involved must have the label multi-repository added in GitHub.

"},{"location":"development/python/","title":"Python library","text":"

The PyCTI library is the official Python client for OpenCTI. It is made to help developers interact with the openCTI plaform.

"},{"location":"development/python/#installation","title":"Installation","text":"

To install the latest Python client library, please use pip:

$ pip3 install pycti\n
"},{"location":"development/python/#using-the-helper-functions","title":"Using the helper functions","text":"

The main class OpenCTIApiClient contains all what you need to interact with the platform, you just have to initialize it.

The following example shows how you create an indicator in OpenCTI using the python library with TLP marking and OpenCTI compatible date format.

from dateutil.parser import parse\nfrom pycti import OpenCTIApiClient\nfrom stix2 import TLP_GREEN\n\n# OpenCTI API client initialization\nopencti_api_client = OpenCTIApiClient(\"https://myopencti.server\", \"mysupersecrettoken\")\n\n# Define an OpenCTI compatible date\ndate = parse(\"2019-12-01\").strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n\n# Get the OpenCTI marking for stix2 TLP_GREEN\nTLP_GREEN_CTI = opencti_api_client.marking_definition.read(id=TLP_GREEN[\"id\"])\n\n# Use the client to create an indicator in OpenCTI\nindicator = opencti_api_client.indicator.create(\n    name=\"C2 server of the new campaign\",\n    description=\"This is the C2 server of the campaign\",\n    pattern_type=\"stix\",\n    pattern=\"[domain-name:value = 'www.5z8.info']\",\n    x_opencti_main_observable_type=\"IPv4-Addr\",\n    valid_from=date,\n    update=True,\n    markingDefinitions=[TLP_GREEN_CTI[\"id\"]],\n)\n
"},{"location":"development/python/#examples","title":"Examples","text":"

A suite of illustrative examples is available in the PyCTI GitHub repository to aid in better understanding.

"},{"location":"reference/api/","title":"GraphQL API","text":"

OpenCTI provides a comprehensive API based on GraphQL, allowing users to perform various actions programmatically. The API enables users to interact with OpenCTI's functionality and data, offering a powerful tool for automation, integration, and customization. All actions that can be performed through the platform's graphical interface are also achievable via the API.

"},{"location":"reference/api/#authentication","title":"Authentication","text":"

Access to the OpenCTI API requires authentication using standard authentication mechanisms. Access rights to data via the API will be determined by the access privileges of the user associated with the API key. For authentication, users need to include the following headers in their API requests:

Content-Type: application/json\nAuthorization: Bearer [API key]\n
"},{"location":"reference/api/#documentation","title":"Documentation","text":"

The OpenCTI API consists of various endpoints corresponding to different functionalities and operations within the platform. These endpoints allow users to perform actions such as querying data, creating or updating entities, and more. Users can refer to the Understand GraphQL section to understand how it works.

Documentation for the OpenCTI API, including schema definitions, the list of filters available and queryable fields, is available through the OpenCTI platform. It can be found on the GraphQL playground. However, query examples and mutation examples are not yet available. In the meantime, users can explore the available endpoints and their functionality by inspecting network traffic in the browser's developer tools or by examining the source code of the Python client.

"},{"location":"reference/api/#understand-graphql","title":"Understand GraphQL","text":"

GraphQL is a powerful query language for APIs that enables clients to request exactly the data they need. Unlike traditional REST APIs, which expose fixed endpoints and return predefined data structures, GraphQL APIs allow clients to specify the shape and structure of the data they require.

"},{"location":"reference/api/#core-concepts","title":"Core concepts","text":""},{"location":"reference/api/#schema-definition-language-sdl","title":"Schema Definition Language (SDL)","text":"

GraphQL APIs are defined by a schema, which describes the types of data that can be queried and the relationships between them. The schema is written using the Schema Definition Language (SDL), which defines types, fields, and their relationships.

"},{"location":"reference/api/#query-language","title":"Query language","text":"

GraphQL uses a query language to request data from the server. Clients can specify exactly which fields they need and how they are related, enabling precise data retrieval without over-fetching or under-fetching.

Example:

query IntrusionSets {\n    intrusionSets (\n        filters:\n        {\n            mode: and,\n            filters: [\n                {\n                    key: \"name\",\n                    values: [\"Ajax Security Team\"],\n                    operator: eq,\n                    mode: or,\n                }\n            ],\n            filterGroups: [],\n        }\n    )\n    {\n        edges{\n            node{\n                id\n                name\n                description\n                externalReferences{\n                    edges{\n                        node{\n                            url\n                            source_name\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n
"},{"location":"reference/api/#resolvers","title":"Resolvers","text":"

Resolvers are functions responsible for fetching the requested data. Each field in the GraphQL schema corresponds to a resolver function, which determines how to retrieve the data from the underlying data sources.

"},{"location":"reference/api/#how-it-works","title":"How it Works","text":"
  1. Schema definition: The API provider defines a GraphQL schema using SDL, specifying the types and fields available for querying.
  2. Query execution: Clients send GraphQL queries to the server, specifying the data they need. The server processes the query, resolves each field, and constructs a response object with the requested data.
  3. Validation and execution: The server validates the query against the schema to ensure it is syntactically and semantically correct. If validation passes, the server executes the query, invoking the appropriate resolver functions to fetch the requested data.
  4. Data retrieval: Resolvers fetch data from the relevant data sources, such as databases, APIs, or other services. They transform the raw data into the shape specified in the query and return it to the client.
  5. Response formation: Once all resolvers have completed, the server assembles the response object containing the requested data and sends it back to the client.
"},{"location":"reference/api/#external-resources","title":"External Resources","text":"

For a more in-depth understanding of GraphQL and its usage, consider exploring the following external resources:

  • GraphQL Official Documentation
  • GraphQL query documentation
  • How to GraphQL
"},{"location":"reference/data-intelligence/","title":"Data intelligence","text":"

As a cyber threat intelligence platform, OpenCTI offers functionalities that enable users to move quickly from raw data to operational intelligence by building up high-quality, structured information.

To do so, the platform provides a number of essential capabilities, such as automated data deduplication, merging of similar entities while preserving relationship integrity, the ability to modulate the confidence levels on your intelligence, and the presence of inference rules to automate the creation of logical relationships among your data.

The purpose of this page is to list the features of the platform that contribute to the intelligibility and quality of intelligence.

"},{"location":"reference/data-intelligence/#deduplication","title":"Deduplication","text":"

The first essential data intelligence mechanism in OpenCTI is the deduplication of information and relations.

This advanced functionality not only enables you to check whether a piece of data, information or a relationship is not a duplicate of an existing element, but will also, under certain conditions, enrich the element already present.

If the new duplicated entity has new content, the pre-existing entity can be enriched with the new information from the duplicate.

It works as follows (see details in the dedicated page):

  • For entities: based on the entity's properties based on a specific ID generated by the \"ID Contributing Properties\" platform (properties listed on the dedicated page).
  • For relationships: based on type, source, target, start time, and stop time.
  • For observables: a specific ID is also generated by the platform, this time based on the specifications of the STIX model.

The ability to update and enrich is determined by the confidence level and quality level of the entities and relationships (see diagram on page deduplication).

"},{"location":"reference/data-intelligence/#merging","title":"Merging","text":"

OpenCTI's merging function is one of the platform's crucial data intelligence elements.

From the Data > Entities tab, this feature lets you merge up to 4 entities of the same type. A parent entity is selected and assigned up to three child entities.

The benefit of this feature is to centralize a number of similar elements from different sources without losing data or degrading the quality of the information. During merging, the platform will create relationships to anchor all the data to the consolidated entity.

This enrichment function consolidates the data and avoids duplication, but above all initiates a structured intelligence process while preserving the integrity of pre-existing relationships as presented here.

"},{"location":"reference/data-intelligence/#confidence-level-and-data-segregation","title":"Confidence level and data segregation","text":"

Another key element of OpenCTI's data intelligence is its ability to apply confidence levels and to segregate the data present in the platform.

The confidence level is directly linked to users and Role Based Access Control. It is applied to a user directly or indirectly via the confidence level of the group to which the user belongs. This element is fundamental as it defines the levels of data manipulation to which the user (real or connector) is entitled.

The correct application of confidence levels is all the more important as it will determine the confidence level of the data manipulated by a user. It is therefore a decisive mechanism, since it underpins the confidence you have in the content of your instance.

While it is important to apply a level of trust to your users or groups, it is also important to define a way of categorizing and protecting your data.

Data segregation makes it possible to apply marking definitions and therefore establish a standardized framework for classifying data.

These marking definitions, like the classic Traffic Light Protocols (TLP) implemented by default in the platform, will determine whether a user can access a specific data set. The marking will be applied at the group level to which the user belongs, which will determine the data to which the user has access and therefore the data that the user can potentially handle.

"},{"location":"reference/data-intelligence/#inference-rules","title":"Inference rules","text":"

In OpenCTI, data intelligence is not just about the ability to segregate, qualify or enrich data. OpenCTI's inference rules enable you to mobilize the data on your platform effectively and operationally.

These predefined rules enable the user to speed up cyber threat management. For example, inferences can be used to automatically identify incidents based on a sighting, to create sightings on observables based on new observed data, to propagate relationships based on an observable, etc.

In all, the platform includes some twenty high-performance inference rules that considerably speed up the analysis and response to threats (see the full list here).

These rules are based on a logical interpretation of the data, resulting in a pre-analysis of the information by creating relationships that will enrich the intelligence in the platform. There are three main benefits: efficiency, completeness and accuracy. These user benefits can be found here.

Note: If these rules are present in the platform, they are not activated by default.

Once activated, they scan all the data in your platform in the background to identify all the existing relationships that meet the conditions of the rules. Then, the rules operate continuously to create relationships. If you deactivate a rule, all the objects and relationships it has created will be deleted.

These actions can only be carried out by an administrator of the instance.

"},{"location":"reference/data-model/","title":"Data model","text":"

Under construction

This page will be automatically generated to reference the platform's data model. We are doing our best to implement this automatic generation as quickly as possible.

"},{"location":"reference/filters-migration/","title":"Filters format migration for OpenCTI 5.12","text":"

The version 5.12 of OpenCTI introduces breaking changes to the filters format used in the API. This documentation describes how you can migrate your scripts or programs that call the OpenCTI API, when updating from a version of OpenCTI inferior to 5.12.

"},{"location":"reference/filters-migration/#why-this-migration","title":"Why this migration?","text":"

Before OpenCTI 5.12, it was not possible to construct complex filters combinations: we couldn't embed filters within filters, used different boolean modes (and/or), filter on all available attributes or relations for a given entity type, or even test for empty fields of any sort.

Legacy of years of development, the former format and filtering mechanics were not adapted for such task, and a profound refactoring was necessary to make it happen.

Here are the main pain points we identified beforehand:

  • The filters frontend and backend formats were very different, requiring careful conversions.
  • The filter were static lists of keys, depending on each given entity type and maintained by hands.
  • The operator (eq, not_eq, etc.) was inside the key (e.g. entity_type_not_eq), limiting operator combination and requiring error-prone parsing.
  • The frontend format imposed a unique form of combination (and between filters, or between values inside each filter, and nothing else possible).
  • The flat list structure made impossible filter imbrication by nature.
  • Filters and query options were mixed in GQL queries for the same purpose (for instance, option types analog to a filter on key entity_type).
// filter formats in OpenCTI < 5.12\n\ntype Filter = {\n    key: string, // a key in the list of the available filter keys for given the entity type\n    values: string[],\n    operator: string,\n    filterMode: 'and' | 'or',\n}\n\n// \"give me Reports labelled with labelX or labelY\"\nconst filters = [\n  { \n    \"key\": \"entity_type\",\n    \"values\": [\"Report\"],\n    \"operator\": \"eq\",\n    \"filterMode\": \"or\"\n  },\n  { \n    \"key\": \"labelledBy\",\n    \"values\": [\"<id-for-labelX>\", \"<id-for-labelY>\"],\n    \"operator\": \"eq\",\n    \"filterMode\": \"or\"\n  },\n]\n

The new format brings a lot of short-term benefits and is compatible with our long-term vision of the filtering capabilities in OpenCTI. We chose a simple recursive structure that allow complex combination of any sort with respect to basic boolean logic.

The list of operator is fixed and can be extended during future developments.

// filter formats in OpenCTI >= 5.12\n\ntype FilterGroup = {\n  mode: 'and' | 'or'\n  filters: Filter[]\n  filterGroups: FilterGroup[] // recursive definition\n}\n\ntype Filter  = {\n  key: string[]\n  values: string[]\n  operator: 'eq' | 'not_eq' | 'gt' // ... and more\n  mode: 'and' | 'or',\n}\n\n// \"give me Reports and RFIs, not marked TLP:RED with labelX or no label\"\nconst filters = {\n  mode: 'and',\n  filters: [\n    { key: 'entity_type', values: ['Report', 'Case-Rfi'], operator: 'eq', mode: 'or', },\n    { key: 'objectMarking', values: ['<id-for-TLP:RED>'], operator: 'not_eq', mode: 'or', },\n  ],\n  filterGroups: [{\n    mode: 'or',\n    filters: [\n      { key: 'objectLabel', values: [\"<id-for-labelX>\"], operator: 'eq', mode: 'or', },\n      { key: 'objectLabel', values: [], operator: 'nil', mode: 'or', },\n    ],\n    filterGroups: [],\n  }],\n};\n

Because changing filters format impacts almost everything in the platform, we decided to do a complete refactoring once and for all. We want this migration process to be clear and easy.

"},{"location":"reference/filters-migration/#what-has-been-changed","title":"What has been changed","text":"

The new filter implementation bring major changes in the way filters are processed and executed.

  • We change the filters formats (see FilterGroup type above):

    • In the frontend, an operator and a mode are stored for each key.
    • The new format enables filters imbrication thanks to the new attribute 'filterGroups'.
    • The keys are of type string (no more static list of enums).
    • The 'values' attribute can no longer contain null values (use the nil operator instead).
  • We also renamed some filter keys, to be consistent with the entities schema definitions.

  • We implemented the handling of the different operators and modes in the backend.
  • We introduced new void operators (nil / not_nil) to test the presence or absence of value in any field.
"},{"location":"reference/filters-migration/#how-to-migrate-your-own-filters","title":"How to migrate your own filters","text":"

We wrote a migration script to convert all stored filters created prior to version 5.12. These filters will thus be migrated automatically when starting your updated platform.

However, you might have your own connectors, queries, or python scripts that use the graphql API or the python client. If this is the case, you must change the filter format if you want to run the code against OpenCTI >= 5.12.

"},{"location":"reference/filters-migration/#filter-conversion","title":"Filter conversion","text":"

To convert filters prior to version 5.12 in the new format:

  1. update the key field if it has been changed in 5.12 (see the conversion table below),
  2. rename the field filterMode in mode
  3. if values contains null, see below for conversion.

Now you can build your new FilterGroup object with:

  • mode: 'and'
  • filters = array of converted filters (following previous steps),
  • filterGroups: []
const oldFilter = {\n    key: 'old_key',\n    values: ['value1', 'value2'],\n    operator: 'XX',\n    filterMode: 'XX',\n}\n\nconst convertedFilter = {\n    key: 'converted_key_if_necessary',\n    values: ['value1', 'value2'],\n    operator: 'XX',\n    mode: 'XX',\n}\n\nconst newFilters = {\n    mode: 'and',\n    filters: [convertedFilter1, convertedFilter2...], // array with all converted filters\n    filterGroups: [],\n}\n

If values contains a null value, you need to convert the filter by using the new nil / not_nil operators. Here's the procedure:

  1. Extract one filter dedicated to null

    • if operator was 'eq', switch to operator: 'nil' / if operator was not_eq, switch to operator = 'not_nil'
    • values = []
  2. Extract another filter for all the other values.

// \"Must have a label that is not Label1 or Label2\"\nconst oldFilter = {\n    key: 'labelledBy',\n    values: [null, 'id-for-Label1', 'id-for-Label2'],\n    operator: 'not_eq',\n    filterMode: 'and',\n}\n\nconst newFilters = {\n    mode: 'and',\n    filters: [\n      {\n        key: 'objectLabel',\n        values: ['id-label-1', 'id-for-Label2'],\n        operator: 'not_eq',\n        mode: 'and',\n      },\n      {\n        key: 'objectLabel',\n        values: [],\n        operator: 'not_nil',\n        mode: 'and',\n      },\n    ],\n    filterGroups: [],\n}\n

Switch to nested filter to preserve logic

To preserve the logic of your old filter you might need to compose nested filter groups. This could happen for instance when using eq operator with null values for one filter, combined in and mode with other filters.

"},{"location":"reference/filters-migration/#filter-keys-conversion-table","title":"Filter keys conversion table","text":"

Make sure you address all the filter keys that require conversion, following the map below:

// array of [oldKey, newKey] for the renamed keys\nconst keyConversion = [\n    ['labelledBy', 'objectLabel'],\n    ['markedBy', 'objectMarking'],\n    ['objectContains', 'objects'],\n    ['killChainPhase', 'killChainPhases'],\n    ['assigneeTo', 'objectAssignee'],\n    ['participant', 'objectParticipant'],\n    ['creator', 'creator_id'],\n    ['hasExternalReference', 'externalReferences'],\n    ['hashes_MD5', 'hashes.MD5'],\n    ['hashes_SHA1', 'hashes.SHA-1'],\n    ['hashes_SHA256', 'hashes.SHA-256'],\n    ['hashes_SHA512', 'hashes.SHA-512']\n]\n
"},{"location":"reference/filters-migration/#examples","title":"Examples","text":""},{"location":"reference/filters-migration/#simple-example","title":"Simple example","text":"

Let's start with a simple case:

All Reports with label \"label1\" or \"label2\"

(entity_type = Report) AND (label = label1 OR label2)

const oldBackendFilter = [\n  {\n    key: 'entity_type',\n    values: ['Report'],\n    operator: 'eq',\n    filterMode: 'or',\n  },\n  {\n   key: 'labelledBy',\n    values: [label1_id, label2_id],\n    operator: 'eq',\n    filterMode: 'or',\n  },\n];\n\nconst newFilters = {\n  mode: 'and',\n  filters: [\n    {\n      key: 'entity_type',\n      values: ['Report'],\n      operator: 'eq',\n      mode: 'or',\n    },\n    {\n      key: 'objectLabel',\n      values: [label1_id, label2_id],\n      operator: 'eq',\n      mode: 'or',\n    }\n  ],\n  filterGroups: [],\n};\n
"},{"location":"reference/filters-migration/#complex-example","title":"Complex example","text":"

Now let's see a more complex case involving filter group nesting:

\"All Reports that have either the label \"label1\" or \"label2\", or no label at all\"

(entity_type = Report) AND (label = \"No label\" OR label1 OR label2)

const oldBackendFilter = [\n    {\n      key: 'labelledBy',\n      values: [label1_id, label2_id, null],\n      operator: 'eq',\n      filterMode: 'or',\n    },\n    {\n      key: 'entity_type',\n      values: ['Report'],\n      operator: 'eq',\n      filterMode: 'or',\n    },\n];\n\nconst newFilters = {\n    mode: 'and',\n    filters: [\n      {\n        key: 'entity_type',\n        values: ['Report'],\n        operator: 'eq',\n        mode: 'or',\n      },\n    ],\n    // the combination mode/operator in this case requires nested filter groups\n    filterGroups: [\n      {\n        mode: 'or',\n        filters: [\n          {\n            key: 'objectLabel',\n            values: [label1_id, label2_id],\n            operator: 'eq',\n            mode: 'or',\n          },\n          {\n            key: 'objectLabel',\n            operator: 'nil',\n            values: [], // empty, does not matter\n            mode: 'or', // and/or, does not matter \n          },\n        ],\n        filterGroups: [],\n      }\n    ],\n};\n
"},{"location":"reference/filters-migration/#residual-dynamic-filters","title":"Residual dynamic filters","text":"

Dynamic filters are not stored in the database, they enable to filter view in the UI, e.g. filters in entities list, investigations, knowledge graphs. They are saved as URL parameters, and can be saved in local storage.

These filters are not migrated automatically and are lost when moving to 5.12. This concerns the filters saved for each view, that are restored when coming back to the same view. You will need to reconstruct the filters by hand in the UI; these new filters will be properly saved and restored afterward.

Also, when going to an url with filters in the old format, OpenCTI will display a warning and remove the filter parameters. Only URLs built by OpenCTI 5.12 are compatible with it, so you will need to reconstruct the filters by hand and save / share your updated links.

"},{"location":"reference/filters/","title":"Filter knowledge","text":"

In OpenCTI, you can filter data to target or view entities that have particular attributes.

"},{"location":"reference/filters/#filters-usages","title":"Filters usages","text":"

There are two types of filters that are used in many locations in the platform:

  • in entities lists: to display only the entities matching the filters. If an export or a background task is generated, only the filtered data will be taken into account,
  • in investigations and knowledge graphs: to display only the entities matching the filters,
  • in dashboards: to create widget with only the entities matching the filters,
  • in feeds, TAXII collections, triggers, streams, playbooks, background tasks: to process only the data or events matching the filters.
"},{"location":"reference/filters/#dynamic-filters","title":"Dynamic filters","text":"

Dynamic filters are not stored in the database, they enable to filter view in the UI, e.g. filters in entities list, investigations, knowledge graphs.

However, they are still persistent in the platform frontend side. The filters used in a view are saved as URL parameters, so you can save and share links of these filtered views.

Also, your web browser saves in local storage the filters that you are setting in various places of the platform, allowing to retrieve them when you come back to the same view. You can then keep working from where you left of.

"},{"location":"reference/filters/#stored-filters","title":"Stored filters","text":"

Stored filters are attributes of an entity, and are therefore stored in the database. They are stored as an attribute in the object itself, e.g. filters in dashboards, feeds, TAXII collections, triggers, streams, playbooks.

"},{"location":"reference/filters/#create-a-filter","title":"Create a filter","text":"

To create a filter, add every key you need using the 'Add filter' select box. It will give you the possible attributes on which you can filter in the current view.

A grey box appears and allows to select:

  1. the operator to use, and
  2. the values to compare (if the operator is not \"empty\" or \"not_empty\").

You can add as many filters as you want, even use the same key twice with different operators and values.

The boolean modes (and/or) are either global (between every attribute filters) or local (between values inside a filter). Both can be switched with a single click, changing the logic of your filtering.

"},{"location":"reference/filters/#filters-format","title":"Filters format","text":"

Since OpenCTI 5.12, the OpenCTI platform uses a new filter format called FilterGroup. The FilterGroup model enables to do complex filters imbrication with different boolean operators, which extends greatly the filtering capabilities in every part of the platform.

"},{"location":"reference/filters/#structure","title":"Structure","text":"

The new format used internally by OpenCTI and exposed through its API, can be described as below:

// filter formats in OpenCTI >= 5.12\n\ntype FilterGroup = {\n  mode: 'and' | 'or'\n  filters: Filter[]\n  filterGroups: FilterGroup[] // recursive definition\n}\n\ntype Filter  = {\n  key: string[] // or single string (input coercion)\n  values: string[]\n  operator: 'eq' | 'not_eq' | 'gt' // ... and more\n  mode: 'and' | 'or',\n}\n
"},{"location":"reference/filters/#properties","title":"Properties","text":"

In a given filter group, the mode (and or or) represents the boolean operation between the different filters and filterGroups arrays. The filters and filterGroups arrays are composed of objects of type Filter and FilterGroup.

The Filter has 4 properties:

  • a key, representing the kind of data we want to target (example: objectLabel to filter on labels or createdBy to filter on the author),
  • an array of values, representing the values we want to compare to,
  • an operator representing the operation we want to apply between the key and the values,
  • a mode (and or or) to apply between the values if there are several ones.
"},{"location":"reference/filters/#operators","title":"Operators","text":"

The available operators are:

Value Meaning Additional information eq equal not_eq different gl greater than against textual values, the alphabetical ordering is used gte greater than or equal against textual values, the alphabetical ordering is used lt lower than against textual values, the alphabetical ordering is used lte lower than or equal against textual values, the alphabetical ordering is used nil empty / no value nil do not require anything inside values not_nil non-empty / any value not_nil do not require anything inside values

In addition, there are operators:

  • starts_with / not_starts_with / ends_with / not_ends_with / contains / not contains, available for searching in short string fields (name, value, title, etc.),
  • search, available in short string and text fields.

There is a small difference between search and contains. search finds any occurrence of specified words, regardless of order, while \"contains\" specifically looks for the exact sequence of words you provide.

Always use single-key filters

Multi-key filters are not supported across the platform and are reserved to specific, internal cases.

"},{"location":"reference/filters/#examples","title":"Examples","text":"

entity_type = 'Report'

filters = {\n    mode: 'and',\n    filters: [{\n        key: 'entity_type',\n        values: ['Report'],\n        operator: 'eq',\n        mode: 'or', // useless here because values contains only one value\n    }],\n    filterGroups: [],\n};\n

(entity_type = 'Report') AND (label = 'label1' OR 'label2')

filters = {\n    mode: 'and',\n    filters: [\n        {\n            key: 'entity_type',\n            values: ['Report'],\n            operator: 'eq',\n            mode: 'or',\n        },\n        {\n            key: 'objectLabel',\n            values: ['label1', 'label2'],\n            operator: 'eq',\n            mode: 'or',\n        }\n    ],\n    filterGroups: [],\n};\n

(entity_type = 'Report') AND (confidence > 50 OR marking is empty)

filters = {\n    mode: 'and',\n    filters: [\n        {\n            key: 'entity_type',\n            values: ['Report'],\n            operator: 'eq',\n            mode: 'or',\n        }\n    ],\n    filterGroups: [\n        {\n            mode: 'or',\n            filters: [\n                {\n                    key: 'confidence',\n                    values: ['50'],\n                    operator: 'gt',\n                    mode: 'or',\n                },\n                {\n                    key: 'objectMarking',\n                    values: [],\n                    operator: 'nil',\n                    mode: 'or',\n                },\n            ],\n            filterGroups: [],\n        }\n    ],\n};\n
"},{"location":"reference/filters/#keys","title":"Keys","text":""},{"location":"reference/filters/#filter-keys-validation","title":"Filter keys validation","text":"

Only a specific set of key can be used in the filters.

Automatic key checking prevents typing error when constructing filters via the API. If a user write an unhandled key (object-label instead of objectLabel for instance), the API will return an error instead of an empty list. Doing so, we make sure the platform do not provide misleading results.

"},{"location":"reference/filters/#regular-filter-keys","title":"Regular filter keys","text":"

For an extensive list of available filter keys, refer to the attributes and relations schema definitions.

Here are some of the most useful keys as example. NB: X refers here to the filtered entities.

  • objectLabel: label applied on X,
  • objectMarking: marking applied on X,
  • createdBy: author of X,
  • creator_id: technical creator of X,
  • created_at: date of creation of X in the platform,
  • confidence: confidence of X,
  • entity_type: entity type of X ('Report', 'Stix-Cyber-Observable', ...),
"},{"location":"reference/filters/#special-filter-keys","title":"Special filter keys","text":"

Some keys do not exist in the schema definition, but are allowed in addition. They describe a special behavior.

It is the case for:

  • sightedBy: entities to which X is linked via a STIX sighting relationship,
  • workflow_id: status id of the entities, or status template id of the status of the entities,
  • representative: entities whose representative (name for reports, value for some observables, composition of the source and target names for a relationship...) matches the filter,
  • connectedToId: the listened instances for an instance trigger.

For some keys, negative equality filtering is not supported yet (not_eq operator). For instance, it is the case for:

  • fromId
  • fromTypes
  • toId
  • toTypes

The regardingOf filter key has a special format and enables to target the entities having a relationship of a certain type with certain entities. Here is an example of filter to fetch the entities related to the entity X:

filters = {\n  mode: 'and',\n  filters: [\n    {\n      key: 'regardingOf',\n      values: [\n        { key: 'id', values: ['id-of-X'] },\n        { key: 'relationship_type', values: ['related-to'] },\n      ],\n    },\n  ],\n  filterGroups: [],\n};\n
"},{"location":"reference/filters/#limited-support-in-stream-events-filtering","title":"Limited support in stream events filtering","text":"

Filters that are run against the event stream are not using the complete schema definition in terms of filtering keys.

This concerns:

  • Live streams,
  • CSV feeds,
  • TAXII collection,
  • Triggers,
  • Playbooks.

For filters used in this context, only some keys are supported for the moment:

  • confidence
  • objectAssignee
  • createdBy
  • creator
  • x_opencti_detection
  • indicator_types
  • objectLabel
  • x_opencti_main_observable_type
  • objectMarking
  • objects
  • pattern_type
  • priority
  • revoked
  • severity
  • x_opencti_score
  • entity_type
  • x_opencti_workflow_id
  • connectedToId (for the instance triggers)
  • fromId (the instance in the \"from\" of a relationship)
  • fromTypes (the entity type in the \"from\" of a relationship)
  • toId (the instance in the \"to\" of a relationship)
  • toTypes (the entity type in the \"to\" of a relationship)
"},{"location":"reference/fips/","title":"SSL FIPS 140-2 deployment","text":""},{"location":"reference/fips/#introduction","title":"Introduction","text":"

For organizations that need to deploy OpenCTI in a SSL FIPS 140-2 compliant environment, we provide FIPS compliant OpenCTI images for all components of the platform. Please note that you will also need to deploy dependencies (ElasticSearch / OpenSearch, Redis, etc.) with FIPS 140-2 SSL to have the full compliant OpenCTI technological stack.

OpenCTI SSL FIPS 140-2 compliant builds

The OpenCTI platform, workers and connectors SSL FIPS 140-2 compliant images are based on packaged Alpine Linux with OpenSSL 3 and FIPS mode enabled maintened by the Filigran engineering team.

"},{"location":"reference/fips/#dependencies","title":"Dependencies","text":""},{"location":"reference/fips/#aws-native-services-in-fedramp-compliant-environment","title":"AWS Native Services in FedRAMP compliant environment","text":"

It is important to remind that OpenCTI is fully compatible with AWS native services and all dependencies are available in both FedRAMP Moderate (East / West) and FedRAMP High (GovCloud) scopes.

  • Amazon OpenSearch Service (OpenSearch)
  • Amazon ElastiCache (Redis)
  • Amazon MQ (RabbitMQ)
  • Amazon Simple Storage Service (S3 bucket)
"},{"location":"reference/fips/#elasticsearch-opensearch","title":"ElasticSearch / OpenSearch","text":"

ElasticSearch is known to be compatible with FIPS 140-2 SSL using the proper JVM. There is a comprehensive guide in the Elastic documentation.

Alternatively, please note that Elastic is also providing an ElasticSearch FedRAMP authorized cloud offering.

"},{"location":"reference/fips/#redis","title":"Redis","text":"

Redis does not provide FIPS 140-2 SSL compliant Docker images but supports very well custom tls-ciphersuites that can be configured to use the system FIPS 140-2 OpenSSL library.

Alternatively, you can use a Stunnel TLS endpoint to ensure encrypted communication between OpenCTI and Redis. There are a few examples available, here or here.

"},{"location":"reference/fips/#rabbitmq","title":"RabbitMQ","text":"

RabbitMQ does not provide FIPS 140-2 SSL compliant Docker images but, as Redis, supports custom cipher suites. Also, it is confirmed since RabbitMQ version 3.12.5, the associated Erlang build (> 26.1), supports FIPS mode on OpenSSL 3.

Alternatively, you can use a Stunnel TLS endpoint to ensure encrypted communication between OpenCTI and RabbitMQ.

"},{"location":"reference/fips/#s3-bucket-minio","title":"S3 Bucket / MinIO","text":"

If you cannot use an S3 endpoint already deployed in your FIPS 140-2 SSL compliant environment, MinIO provides FIPS 140-2 SSL compliant Docker images which then are very easy to deploy within your environment.

"},{"location":"reference/fips/#opencti-stack","title":"OpenCTI stack","text":""},{"location":"reference/fips/#platform","title":"Platform","text":"

For the platform, we provide FIPS 140-2 SSL compliant Docker images. Just use the appropriate tag to ensure you are deploying the FIPS compliant version and follow the standard Docker deployment procedure.

"},{"location":"reference/fips/#worker","title":"Worker","text":"

For the worker, we provide FIPS 140-2 SSL compliant Docker images. Just use the appropriate tag to ensure you are deploying the FIPS compliant version and follow the standard Docker deployment procedure.

"},{"location":"reference/fips/#connectors","title":"Connectors","text":"

All connectors have FIPS 140-2 SSL compliant Docker images. For each connector you need to deploy, please use the tag {version}-fips instead of {version} and follow the standard deployment procedure. An example is available on Docker Hub.

"},{"location":"reference/streaming/","title":"Data Streaming","text":""},{"location":"reference/streaming/#presentation","title":"Presentation","text":"

In order to provide a real time way to consume STIX CTI information, OpenCTI provides data events in a stream that can be consumed to react on creation, update, deletion and merge. This way of getting information out of OpenCTI is highly efficient and already use by some connectors.

"},{"location":"reference/streaming/#technology","title":"Technology","text":""},{"location":"reference/streaming/#redis-stream","title":"Redis stream","text":"

OpenCTI is currently using REDIS Stream as the technical layer. Each time something is modified in the OpenCTI database, a specific event is added in the stream.

"},{"location":"reference/streaming/#sse-protocol","title":"SSE protocol","text":"

In order to provide a really easy consuming protocol we decide to provide a SSE (https://fr.wikipedia.org/wiki/Server-sent_events) HTTP URL linked to the standard login system of OpenCTI. Any user with the correct access rights can open and access http://opencti_instance/stream, and open an SSE connection to start receiving live events. You can of course consume directly the stream in Redis, but you will have to manage access and rights directly.

"},{"location":"reference/streaming/#events-format","title":"Events format","text":"
id: {Event stream id} -> Like 1620249512318-0\nevent: {Event type} -> create / update / delete\ndata: { -> The complete event data\n    version -> The version number of the event\n    type -> The inner type of the event\n    scope -> The scope of the event [internal or external]\n    data: {STIX data} -> The STIX representation of the data.\n    message -> A simple string to easy understand the event\n    origin: {Data Origin} -> Complex object with different information about the origin of the event\n    context: {Event context} -> Complex object with meta information depending of the event type\n}\n

It can be used to consume the stream from this specific point.

"},{"location":"reference/streaming/#stix-data","title":"STIX data","text":"

The current stix data representation is based on the STIX 2.1 format using extension mechanism. Please take a look to the STIX documentation for more information.

"},{"location":"reference/streaming/#create","title":"Create","text":"

It's simply the data created in STIX format.

"},{"location":"reference/streaming/#delete","title":"Delete","text":"

It's simply the data in STIX format just before his deletion. You will also find the automated deletions in context due to automatic dependency management.

{\n  \"context\": {\n      \"deletions\": [{STIX data}]\n  }\n}\n
"},{"location":"reference/streaming/#update","title":"Update","text":"

This event type publish the complete STIX data information along with patches information. Thanks to the patches, it's possible to rebuild the previous version and easily understand what happens in the update.

Patch and reverse_patch follow the official jsonpatch specification. You can find more information on the jsonpatch page

{\n  \"context\": {\n      \"patch\": [/* patch operation object */],\n      \"reverse_patch\": [/* patch operation object */]\n  }\n}\n
"},{"location":"reference/streaming/#merge","title":"Merge","text":"

Merge is a combination of an update of the merge targets and deletions of the sources. In this event you will find the same patch and reverse_patch as an update and the list of elements merged into the target in the \"sources\" attribute.

{\n  \"context\": {\n      \"patch\": [/* patch operation object */],\n      \"reverse_patch\": [/* patch operation object */],\n      \"sources\": [{STIX data}]\n  }\n}\n
"},{"location":"reference/streaming/#stream-types","title":"Stream types","text":"

In OpenCTI we propose 2 types of streams.

"},{"location":"reference/streaming/#base-stream","title":"Base stream","text":"

The stream hosted in /stream url contains all the raw events of the platform, always filtered by the user rights (marking based). It's a technical stream a bit complex to used but very useful for internal processing or some specific connectors like backup/restore.

This stream is live by default but if, you want to catchup, you can simply add the from parameter to your query. This parameter accept a timestamp in millisecond and also an event id, e.g. http://localhost/stream?from=1620249512599

Stream size?

The raw stream is really important in the platform and needs te be sized according to the period of retention you want to ensure. More retention you will have, more security about reprocessing the past information you will get. We usually recommand 1 month of retention, that usually match 2 000 000 of events. This limit can be configured with redis:trimming option, please check deployment configuration page.

"},{"location":"reference/streaming/#live-stream","title":"Live stream","text":"

This stream aims to simplify your usage of the stream through the connectors, providing a way to create stream with specific filters through the UI. After creating this stream, is simply accessible from /stream/{STREAM_ID}.

It's very useful for various cases of data externalization, synchronization, like SPLUNK, TANIUM...

This stream provides different interesting mechanics:

  • Stream the initial list of instances matching your filters when connecting based on main database if you use the recover parameter
  • Auto dependencies resolution to guarantee the consistency of the information distributed
  • Automatic events translation depending on the element segregation

If you want to dig in about the internal behavior you can check this complete diagram:

"},{"location":"reference/streaming/#general-options","title":"General options","text":"
  • no-dependencies (query parameter or header, default false). Can be used to prevent the auto dependencies resolution. To be used with caution.
  • listen-delete (query parameter or header, default true). Can be used prevent receive deletion events. To be used with caution.
  • with-inferences (query parameter or header, default false). Can be used to add inferences events (from rule engine) in the stream.
"},{"location":"reference/streaming/#from-and-recover","title":"From and Recover","text":"

From and recover are 2 different options that need to be explains.

  • from (query parameter) is always the parameter that describe the initial date/event_id you want to start from. Can also be setup with request header from or last-event-id
  • recover (query parameter) is an option that let you consume the initial event from the database and not from the stream. Can also be setup with request header recover or recover-date

This difference will be transparent for the consumer but very important to get old information as an initial snapshot. This also let you consume information that is no longer in the stream retention period.

The next diagram will help you to understand the concept:

"},{"location":"reference/taxonomy/","title":"Taxonomy","text":"

In OpenCTI, taxonomies serve as structured classification systems that aid in organizing and categorizing intelligence data. This reference guide provides an exhaustive description of the platform's customizable fields within the taxonomies' framework. Users can modify, add, or delete values within the available vocabularies to tailor the classification system to their specific requirements.

For broader documentation on the taxonomies section, please consult the appropriate page.

"},{"location":"reference/taxonomy/#vocabularies","title":"Vocabularies","text":"

Default values are based on those defined in the STIX standard but can be tailored to better suit the organization's needs.

Name Used in Default value Account type vocabulary (account-type-ov) User account facebook, ldap, nis, openid, radius, skype, tacacs, twitter, unix, windows-domain, windows-local Attack motivation vocabulary (attack-motivation-ov) Threat actor group accidental, coercion, dominance, ideology, notoriety, organizational-gain, personal-gain, personal-satisfaction, revenge, unpredictable Attack resource level vocabulary (attack-resource-level-ov) Threat actor group club, contest, government, individual, organization, team Case priority vocabulary (case_priority_ov) Incident response P1, P2, P3, P4 Case severity vocabulary (case_severity_ov) Incident response critical, high, medium, low Channel type vocabulary (channel_type_ov) Channel Facebook, Twitter Collection layers vocabulary (collection_layers_ov) Data source cloud-control-plane, container, host, network, OSINT Event type vocabulary (event_type_ov) Event conference, financial, holiday, international-summit, local-election, national-election, sport-competition Eye color vocabulary (eye_color_ov) Threat actor individual black, blue, brown, green, hazel, other Gender vocabulary (gender_ov) Threat actor individual female, male, nonbinary, other Grouping context vocabulary (grouping_context_ov) Grouping malware-analysis, suspicious-activity, unspecified Hair color vocabulary vocabulary (hair_color_ov) Threat actor individual bald, black, blond, blue, brown, gray, green, other, red Implementation language vocabulary (implementation_language_ov) Malware applescript, bash, c, c++, c#, go, java, javascript, lua, objective-c, perl, php, powershell, python, ruby, scala, swift, typescript, visual-basic, x86-32, x86-64 Incident response type vocabulary (incident_response_type_ov) Incident response data-leak, ransomware Incident severity vocabulary (incident_severity_ov) Incident critical, high, medium, low Incident type vocabulary (incident_type_ov) Incident alert, compromise, cybercrime, data-leak, information-system-disruption, phishing, reputation-damage, typosquatting Indicator type vocabulary (indicator_type_ov) Indicator anomalous-activity, anonymization, attribution, benign, compromised, malicious-activity, unknown Infrastructure type vocabulary (infrastructure_type_ov) Infrastructure amplification, anonymization, botnet, command-and-control, control-system, exfiltration, firewall, hosting-malware, hosting-target-lists, phishing, reconnaissance, routers-switches, staging, unknown, workstation Integrity level vocabulary (integrity_level_ov) Process high, medium, low, system Malware capabilities vocabulary (malware_capabilities_ov) Malware accesses-remote-machines, anti-debugging, anti-disassembly, anti-emulation, anti-memory-forensics, anti-sandbox, anti-vm, captures-input-peripherals, captures-output-peripherals, captures-system-state-data, cleans-traces-of-infection, commits-fraud, communicates-with-c2, compromises-data-availability, compromises-data-integrity, compromises-system-availability, controls-local-machine, degrades-security-software, degrades-system-updates, determines-c2-server, emails-spam, escalates-privileges, evades-av, exfiltrates-data, fingerprints-host, hides-artifacts, hides-executing-code, infects-files, infects-remote-machines, installs-other-components, persists-after-system-reboot, prevents-artifact-access, prevents-artifact-deletion, probes-network-environment, self-modifies, steals-authentication-credentials, violates-system-operational-integrity Malware result vocabulary (malware_result_ov) Malware analysis benign, malicious, suspicious, unknown Malware type vocabulary (malware_type_ov) Malware adware, backdoor, bootkit, bot, ddos, downloader, dropper, exploit-kit, keylogger, ransomware, remote-access-trojan, resource-exploitation, rogue-security-software, rootkit, screen-capture, spyware, trojan, unknown, virus, webshell, wiper, worm Marital status vocabulary (marital_status_ov) Threat actor individual annulled, divorced, domestic_partner, legally_separated, married, never_married, polygamous, separated, single, widowed Note types vocabulary (note_types_ov) Note analysis, assessment, external, feedback, internal Opinion vocabulary (opinion_ov) Opinion agree, disagree, neutral, strongly-agree, strongly-disagree Pattern type vocabulary (pattern_type_ov) Indicator eql, pcre, shodan, sigma, snort, spl, stix, suricata, tanium-signal, yara Permissions vocabulary (permissions_ov) Attack pattern Administrator, root, User Platforms vocabulary (platforms_ov) Data source android, Azure AD, Containers, Control Server, Data Historian, Engineering Workstation, Field Controller/RTU/PLC/IED, Google Workspace, Human-Machine Interface, IaaS, Input/Output Server, iOS, linux, macos, Office 365, PRE, SaaS, Safety Instrumented System/Protection Relay, windows Processor architecture vocabulary (processor_architecture_ov) Malware alpha, arm, ia-64, mips, powerpc, sparc, x86, x86-64 Reliability vocabulary (reliability_ov) Report, Organization A - Completely reliable, B - Usually reliable, C - Fairly reliable, D - Not usually reliable, E - Unreliable, F - Reliability cannot be judged Report types vocabulary (report_types_ov) Report internal-report, threat-report Request for information types vocabulary (request_for_information_types_ov) Request for information none Request for takedown types vocabulary (request_for_takedown_types_ov) Request for takedown brand-abuse, phishing Service status vocabulary (service_status_ov) Process SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED, SERVICE_RUNNING, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_STOPPED Service type vocabulary (service_type_ov) Process SERVICE_FILE_SYSTEM_DRIVER, SERVICE_KERNEL_DRIVER, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS Start type vocabulary (start_type_ov) Process SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_SYSTEM_ALERT Threat actor group role vocabulary (threat_actor_group_role_ov) Threat actor group agent, director, independent, infrastructure-architect, infrastructure-operator, malware-author, sponsor Threat actor group sophistication vocabulary (threat_actor_group_sophistication_ov) Threat actor group advanced, expert, innovator, intermediate, minimal, none, strategic Threat actor group type vocabulary (threat_actor_group_type_ov) Threat actor group activist, competitor, crime-syndicate, criminal, hacker, insider-accidental, insider-disgruntled, nation-state, sensationalist, spy, terrorist, unknown Threat actor individual role vocabulary (threat_actor_individual_role_ov) Threat actor individual agent, director, independent, infrastructure-architect, infrastructure-operator, malware-author, sponsor Threat actor individual sophistication vocabulary (threat_actor_individual_sophistication_ov) Threat actor individual advanced, expert, innovator, intermediate, minimal, none, strategic Threat actor individual type vocabulary (threat_actor_individual_type_ov) Threat actor individual activist, competitor, crime-syndicate, criminal, hacker, insider-accidental, insider-disgruntled, nation-state, sensationalist, spy, terrorist, unknown Tool types vocabulary (tool_types_ov) Tool credential-exploitation, denial-of-service, exploitation, information-gathering, network-capture, remote-access, unknown, vulnerability-scanning

"},{"location":"reference/taxonomy/#customization","title":"Customization","text":"

Users can customize the taxonomies by modifying the available values or adding new ones. These modifications enable users to adapt the classification system to their specific intelligence requirements. Additionally, within each vocabulary list, users have the flexibility to customize the order of the dropdown menu associated with the taxonomy. This feature allows users to prioritize certain values or arrange them in a manner that aligns with their specific classification needs. Additionally, users can track the usage count for each vocabulary, providing insights into the frequency of usage and helping to identify the most relevant and impactful classifications. These customization options empower users to tailor the taxonomy system to their unique intelligence requirements, enhancing the efficiency and effectiveness of intelligence analysis within the OpenCTI platform.

"},{"location":"reference/usage-telemetry/","title":"Usage telemetry","text":"

The application collects statistical data related to its usage and performances.

Confidentiality

The OpenCTI platform does not collect any information related to threat intelligence knowledge which remains strictly confidential. Also, the collection is strictly anonymous and personally identifiable information is NOT collected (including IP addresses).

All data collected is anonymized and aggregated to protect the privacy of individual users, in compliance with all privacy regulations.

"},{"location":"reference/usage-telemetry/#purpose-of-the-telemetry","title":"Purpose of the telemetry","text":"

The collected data is used for the following purposes:

  • Improving the functionality and performance of the application.
  • Analyzing user behavior to enhance user experience.
  • Generating aggregated and anonymized statistics for internal and external reporting.
"},{"location":"reference/usage-telemetry/#important-thing-to-know","title":"Important thing to know","text":"

The platform send the metrics to the hostname telemetry.filigran.io using the OTLP protocol (over HTTPS). The format of the data is OpenTelemetry JSON.

The metrics push is done every 6 hours if OpenCTI was able to connect to the hostname when the telemetry manager is started. Metrics are also written in specific logs files in order to be included in support package

"},{"location":"reference/usage-telemetry/#telemetry-metrics","title":"Telemetry metrics","text":"

The application collects statistical data related to its usage. Here are an exhaustive list of the collected metrics:

  • The current platform version
  • The platform unique identifier
  • The platform creation date
  • The number of active users
  • The number of total users
  • The number of nodes in the platform
  • Enterprise Edition status (activated or not)
  • The number of active connectors
"},{"location":"usage/ask-ai/","title":"Ask AI","text":"

Enterprise edition

Ask AI is available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all information

"},{"location":"usage/ask-ai/#prerequisites-for-using-ask-ai","title":"Prerequisites for using Ask AI","text":"

There are several possibilities for Enterprise Edition customers to use OpenCTI AI endpoints:

  • Use the Filigran AI Service leveraging our custom AI model using the token given by the support team.
  • Use OpenAI or MistralAI cloud endpoints using your own tokens.
  • Deploy or use local AI endpoints (Filigran can provide you with the custom model).

Please read the configuration documentation

Beta Feature

Ask AI is a beta feature as we are currently fine-tuning our models. Consider checking important information.

"},{"location":"usage/ask-ai/#how-it-works","title":"How it works","text":"

Even if in the future, we would like to leverage AI to do RAG, for the moment we are mostly using AI to analyze and produce texts or images, based on data directly sent into the prompt.

This means that if you are using Filigran AI endpoint or a local one, your data is never used to re-train or adapt the model and everything relies on a pre-trained and fixed model. When using the Ask AI button in the platform, a prompt is generated with the proper instruction to generate the expected result and use it in the context of the button (in forms, rich text editor etc.).

"},{"location":"usage/ask-ai/#filigran-custom-model","title":"Filigran custom model","text":"

We are hosting a scalable AI endpoint for all SaaS or On-Prem enterprise edition customers, this endpoint is based on MistralAI with a model that will be adapted over time to be more effective when processing threat intelligence related contents.

The model, which is still in beta version, will be adapted in the upcoming months to reach maturity at the end of 2024. It can be shared with on-prem enterprise edition customers under NDA.

"},{"location":"usage/ask-ai/#functionalities-of-ask-ai","title":"Functionalities of Ask AI","text":"

Ask AI is represented by a dedicated icon wherever on of its functionalities is available to use.

"},{"location":"usage/ask-ai/#assistance-for-writing-meaningful-content","title":"Assistance for writing meaningful content","text":"

Ask AI can assist you for writing better textual content, for example better title, name, description and detailed content of Objects.

  • Fix spelling & grammar: try to improve the text from a formulation and grammar perspective.
  • Make it shorter/longer: try to shorten or lengthen the text.
  • Change tone: try to change the tone of the text. You can select if you want the text to be written for Strategic (Management, decision makers), Tactical (for team leaders) or Operational (for technical CTI analysts) audiences.
  • Summarize: try to summarize the text in bullet points.
  • Explain: try to explain the context of the subject's text based on what is available to the LLM.
"},{"location":"usage/ask-ai/#assistance-for-importing-data-from-documents","title":"Assistance for importing data from documents","text":"

Fom the Content tab of a Container (Reports, Groupings and Cases), Ask AI can also assist you for importing data contained in uploaded documents into OpenCTI for further exploitation.

  • Generate report document: Generate a text report based on the knowledge graph (entities and relationships) of this container.
  • Summarize associated files: Generate a summary of the selected files (or all files associated to this container).
  • Try to convert the selected files (or all files associated to this container) in a STIX 2.1 bundle you will then be able to use at your convenience (for example importing it into the platform).

A short video on the FiligranHQ YouTube channel presents tha capabilities of AskAI: https://www.youtube.com/watch?v=lsP3VVsk5ds.

"},{"location":"usage/ask-ai/#improving-generated-elements-of-ask-ai","title":"Improving generated elements of Ask AI","text":"

Be aware that the text quality is highly dependent on the capabilities of the associated LLM.

That is why every generated text by Ask AI is provided in a dedicated panel, allowing you to verify and rectify any error the LLM could have made.

"},{"location":"usage/automation/","title":"Playbooks Automation","text":"

Enterprise edition

Playbooks automation is available under the \"OpenCTI Enterprise Edition\" license. Please read the dedicated page to have all information.

OpenCTI playbooks are flexible automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform.

Playbook automation is accessible in the user interface under Data > Processing > Automation.

Right needed

You need the \"Manage credentials\" capability to use the Playbooks automation, because you will be able to manipulate data simple users cannot access.

You will then be able to:

  • add labels depending on enrichment results to be used in threat intelligence-driven detection feeds,
  • create reports and cases based on various criteria,
  • trigger enrichments or webhooks in given conditions,
  • modify attributes such as first_seen and last_seen based on other pieces of knowledge,
  • etc.
"},{"location":"usage/automation/#playbook-philosophy","title":"Playbook philosophy","text":"

Consider Playbook as a STIX 2.1 bundle pipeline.

Initiating with a component listening to a data stream, each subsequent component in the playbook processes a received STIX bundle. These components have the ability to modify the bundle and subsequently transmit the altered result to connected components.

In this paradigm, components can send out the STIX 2.1 bundle to multiple components, enabling the development of multiple branches within your playbook.

A well-designed playbook end with a component executing an action based on the processed information. For instance, this may involve writing the STIX 2.1 bundle in a data stream.

Validate ingestion

The STIX bundle processed by the playbook won't be written in the platform without specifying it using the appropriate component, i.e. \"Send for ingestion\".

"},{"location":"usage/automation/#create-a-playbook","title":"Create a Playbook","text":"

It is possible to create as many playbooks as needed which are running independently. You can give a name and description to each playbook.

The first step to define in the playbook is the \u201ctriggering event\u201d, which can be any knowledge event (create, update or delete) with customizable filters. To do so, click on the grey rectangle in the center of the workspace and choose the component to \"listen knowledge events\". Configure it with adequate filters. You can use same filters as in other part of the platform.

Then you have flexible choices for the next steps to:

  • filter the initial knowledge,
  • enrich data using external sources and internal rules,
  • modify entities and relationships by applying patches,
  • write the data, send notifications,
  • etc.

Do not forget to start your Playbook when ready, with the Start option of the burger button placed near the name of your Playbook.

By clicking the burger button of a component, you can replace it by another one.

By clicking on the arrow icon in the bottom right corner of a component, you can develop a new branch at the same level.

By clicking the \"+\" button on a link between components, you can insert a component between the two.

"},{"location":"usage/automation/#components-of-playbooks","title":"Components of playbooks","text":""},{"location":"usage/automation/#log-data-in-standard-output","title":"Log data in standard output","text":"

Will write the received STIX 2.1 bundle in platform logs with configurable log level and then send out the STIX 2.1 bundle unmodified.

"},{"location":"usage/automation/#send-for-ingestion","title":"Send for ingestion","text":"

Will pass the STIX 2.1 bundle to be written in the data stream. This component has no output and should end a branch of your playbook.

"},{"location":"usage/automation/#filter-knowledge","title":"Filter Knowledge","text":"

Will allow you to define filter and apply it to the received STIX 2.1 bundle. The component has 2 output, one for data matching the filter and one for the remainder.

By default, filtering is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

"},{"location":"usage/automation/#enrich-through-connector","title":"Enrich through connector","text":"

Will send the received STIX 2.1 bundle to a compatible enrichment connector and send out the modified bundle.

"},{"location":"usage/automation/#manipulate-knowledge","title":"Manipulate knowledge","text":"

Will add, replace or remove compatible attribute of the entities contains in the received STIX 2.1 bundle and send out the modified bundle.

By default, modification is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

"},{"location":"usage/automation/#container-wrapper","title":"Container wrapper","text":"

Will modify the received STIX 2.1 bundle to include the entities into an container of the type you configured. By default, wrapping is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

"},{"location":"usage/automation/#share-with-organizations","title":"Share with organizations","text":"

Will share every entity in the received STIX 2.1 bundle with Organizations you configured. Your platform needs to have declared a platform main organization in Settings/Parameters.

"},{"location":"usage/automation/#apply-predefined-rule","title":"Apply predefined rule","text":"

Will apply a complex automation built-in rule. This kind of rule might impact performance. Current rules are:

  • First/Last seen computing extension from report publication date: will populate first seen and last seen date of entities contained in the report based on its publication date,
  • Resolve indicators based on observables (add in bundle): will retrieve all indicators linked to the bundle's observables from the database,
  • Resolve observables an indicator is based on (add in bundle): retrieve all observables linked to the bundle's indicator from the database,
  • Resolve container references (add in bundle): will add to the bundle all the relationships and entities the container contains (if the entity having triggered the playbook is not a container, the output of this component will be empty),
  • Resolve neighbors relations and entities (add in bundle): will add to the bundle all relations of the entity having triggered the playbook, as well as all entities at the end of these relations, i.e. the \"first neighbors\" (if the entity is a container, the output of this component will be empty).
"},{"location":"usage/automation/#send-to-notifier","title":"Send to notifier","text":"

Will generate a Notification each time a STIX 2.1 bundle is received.

"},{"location":"usage/automation/#promote-observable-to-indicator","title":"Promote observable to indicator","text":"

Will generate indicator based on observables contained in the received STIX 2.1 bundle.

By default, it is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all observables in the bundle (e.g. observables that might result from predefined rule).

You can also add all indicators and relationships generated by this component in the entity having triggered the playbook, if this entity is a container.

"},{"location":"usage/automation/#extract-observables-from-indicator","title":"Extract observables from indicator","text":"

Will extract observables based on indicators contained in the received STIX 2.1 bundle.

By default, it is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all indicators in the bundle (e.g. indicators that might result from enrichment.

You can also add all observables and relationships generated by this component in the entity having triggered the playbook, if this entity is a container.

"},{"location":"usage/automation/#reduce-knowledge","title":"Reduce Knowledge","text":"

Will elagate the received STIX 2.1 bundle based on the configured filter.

"},{"location":"usage/automation/#monitor-playbook-activity","title":"Monitor playbook activity","text":"

At the top right of the interface, you can access execution trace of your playbook and consult the raw data after every step of your playbook execution.

"},{"location":"usage/background-tasks/","title":"Background tasks","text":"

Three types of tasks are done in the background:

  • rule tasks,
  • knowledge tasks,
  • user tasks.

Rule tasks can be seen and activated in Settings > Customization > Rules engine. Knowledge and user tasks can be seen and managed in Data > Background Tasks. The scope of each task is indicated.

"},{"location":"usage/background-tasks/#rule-tasks","title":"Rule tasks","text":"

If a rule task is enabled, it leads to the scan of the whole platform data and the creation of entities or relationships in case a configuration corresponds to the tasks rules. The created data are called 'inferred data'. Each time an event occurs in the platform, the rule engine checks if inferred data should be updated/created/deleted.

"},{"location":"usage/background-tasks/#knowledge-tasks","title":"Knowledge tasks","text":"

Knowledge tasks are background tasks updating or deleting entities and correspond to mass operations on these data. To create one, select entities via the checkboxes in an entity list, and choose the action to perform via the toolbar.

"},{"location":"usage/background-tasks/#rights","title":"Rights","text":"
  • To create a knowledge task, the user should have the capability to Update Knowledge (or the capability to delete knowledge if the task action is a deletion).
  • To see a knowledge task in the Background task section, the user should be the creator of the task, or have the KNOWLEDGE capability.
  • To delete a knowledge task from the Background task section, the user should be the creator of the task, or have the KNOWLEDGE_UPDATE capability.
"},{"location":"usage/background-tasks/#user-tasks","title":"User tasks","text":"

User tasks are background tasks updating or deleting notifications. It can be done from the Notification section, by selecting several notifications via the checkboxes, and choosing an action via the toolbar.

"},{"location":"usage/background-tasks/#rights_1","title":"Rights","text":"
  • A user can create a user task on its own notifications only.
  • To see or delete a user task, the user should be the creator of the task or have the SET_ACCESS capability.
"},{"location":"usage/case-management/","title":"Case management","text":""},{"location":"usage/case-management/#why-case-management","title":"Why Case management?","text":"

Compiling CTI data in one place, deduplicate and correlate to transform it into Intelligence is very important. But ultimately, you need to act based on this Intelligence. Some situations will need to be taken care of, like cybersecurity incidents, requests for information or requests for takedown. Some actions will then need to be traced, to be coordinated and oversaw. Some actions will include feedback and content delivery.

OpenCTI includes Cases to allow organizations to manage situations and organize their team's work. Better, by doing Case management in OpenCTI, you handle your cases with all the context and Intelligence you need, at hand.

"},{"location":"usage/case-management/#how-to-manage-your-case-in-opencti","title":"How to manage your Case in OpenCTI?","text":"

Multiple situations can be modelize in OpenCTI as a Case, either an Incident Response, a Request for Takedown or a Request for Information.

All Cases can contain any entities and relationships you need to represent the Intelligence context related to the situation. At the beginning of your case, you may find yourself with only some Observables sighted in a system. At the end, you may have Indicators, Threat Actor, impacted systems, attack patterns. All representing your findings, ready to be presented and exported as graph, pdf report, timeline, etc.

Some Cases may need some collaborative work and specific Tasks to be performed by people that have relevant skillsets. OpenCTI allows you to associate Tasks in your Cases and assign them to users in the platform. As some type of situation may need the same tasks to be done, it is also possible to pre-define lists of tasks to be applied on your case. You can define these lists by accessing the Settings/Taxonomies/Case templates panel. Then you just need to add it from the overview of your desire Case.

Tip: A user can have a custom dashboard showing him all the tasks that have been assigned to him.

As with other objects in OpenCTI, you can also leverage the Notes to add some investigation and analysis related comments, helping you shaping up the content of your case with unstructured data and trace all the work that have been done.

You can also use Opinions to collect how the Case has been handled, helping you to build Lessons Learned.

To trace the evolution of your Case and define specific resolution worflows, you can use the Status (that can be define in Settings/Taxonomies/Status templates).

At the end of your Case, you will certainly want to report on what has been done. OpenCTI allows you to export the content of the Case in a simple but customizable PDF (currently in refactor). But of course, your company has its own documents' templates, right? With OpenCTI, you will be able to include some nice graphics in it. For example, a Matrix view of the attacker attack pattern or even a graph display of how things are connected.

Also, we are currently working a more meaningfull Timeline view that will be possible to export too.

"},{"location":"usage/case-management/#use-case-example-a-suspicious-observable-is-sighted-by-a-defense-system-is-it-important","title":"Use case example: A suspicious observable is sighted by a defense system. Is it important?","text":"
  • Daily, your SIEM and EDR are fed Indicators of Compromise from your OpenCTI instance.
  • Today, your SIEM has sighted the domain name \"bad.com\" matching one of them. Its alert has been transfered to OpenCTI and has created a Sighting relationship between your System \"SIEM permiter A\" and the Observable \"bad.com\".
  • You are alerted immediatly, because you have activated the inference rule creating a corresponding Incident in this situation, and you have created an alert based on new Incident that sends you email notification and Teams message (webhook).
  • In OpenCTI, you can clearly see the link between the alerting System, the sighted Observable and the corresponding Indicator. Better, you can also see all the context of the Indicator. It is linked to a notorious and recent phishing campaign targeting your activity sector. \"bad.com\" is clearly something to investigate ASAP.
  • You quickly select all the context you have found, and add it to a new Incident responsecase. You position the priority to High, regarding the context, and the severity to Low, as you don't know yet if someone really interacted with \"bad.com\".
  • You also assign the case to one of your colleagues, on duty for investigative work. To guide him, you also create a Task in your case for verifying if an actual interaction happened with \"bad.com\".
"},{"location":"usage/containers/","title":"Containers","text":""},{"location":"usage/containers/#stix-standard","title":"STIX standard","text":""},{"location":"usage/containers/#definition","title":"Definition","text":"

In the STIX 2.1 standard, some STIX Domain Objects (SDO) can be considered as \"container of knowledge\", using the object_refs attribute to refer multiple other objects as nested references. In object_refs, it is possible to refer to entities and relationships.

"},{"location":"usage/containers/#example","title":"Example","text":"
{\n   \"type\": \"report\",\n   \"spec_version\": \"2.1\",\n   \"id\": \"report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3\",\n   \"created_by_ref\": \"identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283\",\n   \"created\": \"2015-12-21T19:59:11.000Z\",\n   \"modified\": \"2015-12-21T19:59:11.000Z\",\n   \"name\": \"The Black Vine Cyberespionage Group\",\n   \"description\": \"A simple report with an indicator and campaign\",\n   \"published\": \"2016-01-20T17:00:00.000Z\",\n   \"report_types\": [\"campaign\"],\n   \"object_refs\": [\n      \"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2\",\n      \"campaign--83422c77-904c-4dc1-aff5-5c38f3a2c55c\",\n      \"relationship--f82356ae-fe6c-437c-9c24-6b64314ae68a\"\n   ]\n}\n

In the previous example, we have a nested reference to 3 other objects:

\"object_refs\": [\n   \"indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2\",\n   \"campaign--83422c77-904c-4dc1-aff5-5c38f3a2c55c\",\n   \"relationship--f82356ae-fe6c-437c-9c24-6b64314ae68a\"\n]\n
"},{"location":"usage/containers/#implementation","title":"Implementation","text":""},{"location":"usage/containers/#types-of-container","title":"Types of container","text":"

In OpenCTI, containers are displayed differently than other entities, because they contain pieces of knowledge. Here is the list of containers in the platform:

Type of entity STIX standard Description Report Native Reports are collections of threat intelligence focused on one or more topics, such as a description of a threat actor, malware, or attack technique, including context and related details. Grouping Native A Grouping object explicitly asserts that the referenced STIX Objects have a shared context, unlike a STIX Bundle (which explicitly conveys no context). Observed Data Native Observed Data conveys information about cyber security related entities such as files, systems, and networks using the STIX Cyber-observable Objects (SCOs). Note Native A Note is intended to convey informative text to provide further context and/or to provide additional analysis not contained in the STIX Objects. Opinion Native An Opinion is an assessment of the correctness of the information in a STIX Object produced by a different entity. Case Extension A case whether an Incident Response, a Request for Information or a Request for Takedown is used to convey an epic with a set of tasks. Task Extension A task, generally used in the context of a case, is intended to convey information about something that must be done in a limited timeframe."},{"location":"usage/containers/#containers-behavior","title":"Containers behavior","text":"

In the platform, it is always possible to visualize the list of entities and/or observables referenced in a container (Container > Entities or Observables) but also to add / remove entities from the container.

As containers can also contain relationships, which are generally linked to the other entities in the container, it is also possible to visualize the container as a graph (Container > Knowledge)

"},{"location":"usage/containers/#containers-of-an-entity-or-a-relationship","title":"Containers of an entity or a relationship","text":"

On the entity or the relationship side, you can always find all containers where the object is contained using the top menu Analysis:

In all containers list, you can also filter containers based on one or multiple contained object(s):

"},{"location":"usage/dashboards-share/","title":"Share custom dashboards","text":"

OpenCTI provides a simple way to share a visualisation of a custom dashboard to anyone, even for people that are outside of the platform. We call those visualisations: public dashboards.

Public dashboards are a snapshot of a custom dashboard at a specific moment of time. By this way you can share a version of a custom dashboard, then modify your custom dashboard without worrying about the impact on public dashboards you have created.

On the contrary, if you want that your public dashboard is updated with the last version of the associated custom dashboard, you can do it with few clicks by recreating a public dashboard using the same name of the one to update.

To be able to share custom dashboards you need to have the Manage data sharing & ingestion capability.

"},{"location":"usage/dashboards-share/#create-a-public-dashboard","title":"Create a public dashboard","text":"

On the top-right of your custom dashboard page you will find a button that will open a panel to manage the public dashboards associated to this custom dashboard.

In this panel you will find two parts: - At the top you have a form allowing you to create public dashboards, - And below, the list of the public dashboards you have created.

"},{"location":"usage/dashboards-share/#form-to-create-a-new-public-dashboard","title":"Form to create a new public dashboard","text":"

First you need to specify a name for your public dashboard. This name will be displayed on the dashboard page. The name is also used to generate an ID for your public dashboard that will be used in the URL to access the dashboard.

The ID is generated as follow: replace all spaces with symbol - and remove special characters. This ID also needs to be unique in the platform as it is used in the URL to access the dashboard.

Then you can choose if the public dashboard is enabled or not. A disabled dashboard means that you cannot access the public dashboard through the custom URL. But you can still manage it from this panel.

Finally you choose the max level of marking definitions for the data to be displayed in the public dashboard. For example if you choose TLP:AMBER then the data fetched by the widgets inside the public dashboard will be at maximum AMBER, you won't retrieved data with RED marking definition.

Also note that the list of marking definitions you can see is based on your current marking access on the platform and the maximum sharable marking definitions defined in your groups.

Define maximum shareable markings in groups

As a platform administrator, you can define, for each group and for each type of marking definition, Maximum marking definitions shareable to be shared through Public Dashboard, regardless of the definition set by users in their public dashboard.

"},{"location":"usage/dashboards-share/#list-of-the-public-dashboards","title":"List of the public dashboards","text":"

When you have created a public dashboard, it will appear in the list below the form.

In this list each item represents a public dashboard you have created. For each you can see its name, path of the URL, max marking definitions, the date of creation, the status to know if the dashboard is enabled or not and some actions.

The possible actions are: copy the link of the public dashboard, disable or enable the dashboard and delete the dashboard.

To share a public dashboard just copy the link and give the URL to the person you want to share with. The dashboard will be visible even if the person is not connected to OpenCTI.

"},{"location":"usage/dashboards/","title":"Custom dashboards","text":"

OpenCTI provides an adaptable and entirely customizable dashboard functionality. The flexibility of OpenCTI's dashboard ensures a tailored and insightful visualization of data, fostering a comprehensive understanding of the platform's knowledge, relationships, and activities.

"},{"location":"usage/dashboards/#dashboard-overview","title":"Dashboard overview","text":"

You have the flexibility to tailor the arrangement of widgets on your dashboard, optimizing organization and workflow efficiency. Widgets can be intuitively placed to highlight key information. Additionally, you can resize widgets from the bottom right corner based on the importance of the information, enabling adaptation to specific analytical needs. This technical flexibility ensures a fluid, visually optimized user experience.

Moreover, the top banner of the dashboard offers a convenient feature to configure the timeframe for displayed data. This can be accomplished through the selection of a relative time period, such as \"Last 7 days\", or by specifying fixed \"Start\" and \"End\" dates, allowing users to precisely control the temporal scope of the displayed information.

"},{"location":"usage/dashboards/#access-control","title":"Access control","text":"

In OpenCTI, the power to create custom dashboards comes with a flexible access control system, allowing users to tailor visibility and rights according to their collaborative needs.

When a user crafts a personalized dashboard, by default, it remains visible only to the dashboard creator. At this stage, the creator holds administrative access rights. However, they can extend access and rights to others using the \"Manage access\" button, denoted by a locker icon, located at the top right of the dashboard page.

Levels of access:

  • View: Access to view the dashboard.
  • Edit: View + Permission to modify and update the dashboard and its widgets.
  • Manage: Edit + Ability to delete the dashboard and to control user access and rights.

It's crucial to emphasize that at least one user must retain admin access to ensure ongoing management capabilities for the dashboard.

Knowledge access restriction

The platform's data access restrictions also apply to dashboards. The data displayed in the widgets is subject to the user's access rights within the platform. Therefore, an admin user will not see the same results in the widgets as a user with limited access, such as viewing only TLP:CLEAR data (assuming the platform contains data beyond TLP:CLEAR).

"},{"location":"usage/dashboards/#share-dashboard-and-widget-configurations","title":"Share dashboard and widget configurations","text":"

OpenCTI provides functionality for exporting, importing and duplicating dashboard and widget configurations, facilitating the seamless transfer of customized dashboard setups between instances or users.

"},{"location":"usage/dashboards/#export","title":"Export","text":"

Dashboards can be exported from either the custom dashboards list or the dashboard view.

To export a dashboard configuration from the custom dashboards list:

  1. Click on the burger menu button at the end of the dashboard line.
  2. Select Export.

To export a widget, the same mechanism is used, but from the burger menu button in the upper right-hand corner of the widget.

To export a dashboard configuration from the dashboard view:

  1. Navigate to the desired dashboard.
  2. Click on the Export button (file with an arrow pointing to the top right corner) located in the top-right corner of the dashboard.

"},{"location":"usage/dashboards/#configuration-file","title":"Configuration file","text":"

The dashboard configuration will be saved as a JSON file, with the title formatted as [YYYYMMDD]_octi_dashboard_[dashboard title]. The expected configuration file content is as follows:

{\n  \"openCTI_version\": \"5.12.0\",\n  \"type\": \"dashboard\",\n  \"configuration\": {\n    \"name\": \"My dashboard title\",\n    \"manifest\": \"eyJ3aWRn(...)uZmlnIjp7fX0=\"\n  }\n}\n

The widget configuration will be saved as a JSON file, with the title formatted as [YYYYMMDD]_octi_widget_[widget view]. The expected configuration file content is as follows:

{\n  \"openCTI_version\": \"5.12.0\",\n  \"type\": \"widget\",\n  \"configuration\": \"eyJ0eXB(...)iOmZhbHNlfX0=\"\n}\n

Source platform-related filters

When exporting a dashboard or widget configuration, all filters will be exported as is. Filters on objects that do not exist in the receiving platform will need manual deletion after import. Filters to be deleted can be identified by their \"delete\" barred value.

"},{"location":"usage/dashboards/#import","title":"Import","text":"

Dashboards can be imported from the custom dashboards list:

  1. Hover over the Add button (+) in the right bottom corner.
  2. Click on the Import dashboard button (cloud with an upward arrow).
  3. Select your file.

To import a widget, the same mechanism is used, but from a dashboard view.

Configuration compatibility

Only JSON files with the required properties will be accepted, including \"openCTI_version: [5.12.0 and above]\", \"type: [dashboard|widget]\", and a \"configuration\". This applies to both dashboards and widgets configurations.

"},{"location":"usage/dashboards/#duplicate","title":"Duplicate","text":"

Dashboards can be duplicated from either the custom dashboards list or the dashboard view.

To duplicate a dashboard from the custom dashboards list:

  1. Click on the burger menu button at the end of the dashboard line.
  2. Select Duplicate.

To duplicate a widget, the same mechanism is used, but from the burger menu button in the upper right-hand corner of the widget.

To duplicate a dashboard from the dashboard view:

  1. Navigate to the desired dashboard.
  2. Click on the Duplicate the dashboard button (two stacked sheets) located in the top-right corner of the dashboard.

Upon successful duplication, a confirmation message is displayed for a short duration, accompanied by a link for easy access to the new dashboard view. Nevertheless, the new dashboard can still be found in the dashboards list.

Dashboard access

The user importing or duplicating a dashboard becomes the only one with access to it. Then, access can be managed as usual.

"},{"location":"usage/data-model/","title":"Data model","text":""},{"location":"usage/data-model/#introduction","title":"Introduction","text":"

The OpenCTI core design relies on the concept of a knowledge graph, where you have two different kinds of object:

  1. Nodes are used to describe entities, which have some properties or attributes.
  2. Edges are used to describe relationships, which are created between two entity nodes and have some properties or attributes.

Example

An example would be that the entity APT28 has a relationship uses to the malware entity Drovorub.

"},{"location":"usage/data-model/#standard","title":"Standard","text":""},{"location":"usage/data-model/#the-stix-model","title":"The STIX model","text":"

To enable a unified approach in the description of threat intelligence knowledge as well as importing and exporting data, the OpenCTI data model is based on the STIX 2.1 standard. Thus we highly recommend to take a look to the STIX Introductory Walkthrough and to the different kinds of STIX relationships to get a better understanding of how OpenCTI works.

Some more important STIX naming shortcuts are:

  • STIX Domain Objects (SDO): Attack Patterns, Malware, Threat Actors, etc.
  • STIX Cyber Observable (SCO): IP Addresses, domain names, hashes, etc.
  • STIX Relationship Object (SRO): Relationships, Sightings

"},{"location":"usage/data-model/#extensions","title":"Extensions","text":"

In some cases, the model has been extended to be able to:

  • Support more types of SCOs to modelize information systems such as cryptocurrency wallets, user agents, etc.
  • Support more types of SDOs to modelize disinformation and cybercrime such as channels, events, narrative, etc.
  • Support more types of SROs to extend the new SDOs such asamplifies, publishes, etc.
"},{"location":"usage/data-model/#implementation-in-the-platform","title":"Implementation in the platform","text":""},{"location":"usage/data-model/#diagram-of-types","title":"Diagram of types","text":"

You can find below the digram of all types of entities and relationships available in OpenCTI.

"},{"location":"usage/data-model/#attributes-and-properties","title":"Attributes and properties","text":"

To get a comprehensive list of available properties for a given type of entity or relationship, you can use the GraphQL playground schema available in your \"Profile > Playground\". Then you can click on schema. You can for instance search for the keyword IntrusionSet:

"},{"location":"usage/dates/","title":"Meaning of dates","text":"

In OpenCTI, entities can contain various dates, each representing different types of information. The available dates vary depending on the entity types.

"},{"location":"usage/dates/#dates","title":"Dates","text":"

In OpenCTI, dates play a crucial role in understanding the context and history of entities. Here's a breakdown of the different dates you might encounter in the platform:

  • \u201cPlatform creation date\u201d: This date signifies the moment the entity was created within OpenCTI. On the API side, this timestamp corresponds to the created_at field. It reflects the initiation of the entity within the OpenCTI environment.
  • \u201cOriginal creation date\u201d: This date reflects the original creation date of the data on the source's side. It becomes relevant if the source provides this information and if the connector responsible for importing the data takes it into account. In cases where the source date is unavailable or not considered, this date defaults to the import date (i.e. the \u201cPlatform creation date\u201d). On the API side, this timestamp corresponds to the created field.
  • \u201cModification date\u201d: This date captures the most recent modification made to the entity, whether a connector automatically modifies it or a user manually edits the entity. On the API side, this timestamp corresponds to the updated_at field. It serves as a reference point for tracking the latest changes made to the entity.
  • Date not shown on GUI: There is an additional date which is not visible on the entity in the GUI. This date is the modified field on the API. This date reflects the original update date of the data on the source's side. The difference between modified and updated_at is identical to the difference between created and created_at.

Understanding these dates is pivotal for contextualizing the information within OpenCTI, ensuring a comprehensive view of entity history and evolution.

"},{"location":"usage/dates/#date-types","title":"Date types","text":""},{"location":"usage/dates/#technical-dates","title":"Technical dates","text":"

The technical dates refer to the dates directly associated to data management within the platform. The API fields corresponding to technical dates are:

  • created_at: Indicates the date and time when the entity was created in the platform.
  • updated_at: Represents the date and time of the most recent update to the entity in the platform.
"},{"location":"usage/dates/#functional-dates","title":"Functional dates","text":"

The functional dates are the dates functionally significant, often indicating specific events or milestones. The API fields corresponding to functional dates are:

  • created: Denotes the date and time when the entity was created on the source's side.
  • modified: Represents the date and time of the most recent modification to the entity on the source's side.
  • start_time: Indicates the start date and time associated with a relationship.
  • stop_time: Indicates the stop date and time associated with a relationship.
  • first_seen: Represents the initial date and time when the entity/activity was observed.
  • last_seen: Represents the most recent date and time when the entity/activity was observed.
"},{"location":"usage/deduplication/","title":"Deduplication","text":"

One of the core concept of the OpenCTI knowledge graph is all underlying mechanisms implemented to accurately de-duplicate and consolidate (aka. upserting) information about entities and relationships.

"},{"location":"usage/deduplication/#creation-behavior","title":"Creation behavior","text":"

When an object is created in the platform, whether manually by a user or automatically by the connectors / workers chain, the platform checks if something already exist based on some properties of the object. If the object already exists, it will return the existing object and, in some cases, update it as well.

Technically, OpenCTI generates deterministic IDs based on the listed properties below to prevent duplicate (aka \"ID Contributing Properties\"). Also, it is important to note that there is a special link between name and aliases leading to not have entities with overlapping aliases or an alias already used in the name of another entity.

"},{"location":"usage/deduplication/#entities","title":"Entities","text":"Type Attributes Area (name OR x_opencti_alias) AND x_opencti_location_type Attack Pattern (name OR alias) AND optional x_mitre_id Campaign name OR alias Channel name OR alias City (name OR x_opencti_alias) AND x_opencti_location_type Country (name OR x_opencti_alias) AND x_opencti_location_type Course Of Action (name OR alias) AND optional x_mitre_id Data Component name OR alias Data Source name OR alias Event name OR alias Feedback Case name AND created (date) Grouping name AND context Incident name OR alias Incident Response Case name OR alias Indicator pattern OR alias Individual (name OR x_opencti_alias) and identity_class Infrastructure name OR alias Intrusion Set name OR alias Language name OR alias Malware name OR alias Malware Analysis name OR alias Narrative name OR alias Note None Observed Data name OR alias Opinion None Organization (name OR x_opencti_alias) and identity_class Position (name OR x_opencti_alias) AND x_opencti_location_type Region name OR alias Report name AND published (date) RFI Case name AND created (date) RFT Case name AND created (date) Sector (name OR alias) and identity_class Task None Threat Actor name OR alias Tool name OR alias Vulnerability name OR alias

Names and aliases management

The name and aliases of an entity define a set of unique values, so it's not possible to have the name equal to an alias and vice versa.

"},{"location":"usage/deduplication/#relationships","title":"Relationships","text":"

The deduplication process of relationships is based on the following criterias:

  • Type
  • Source
  • Target
  • Start time between -30 days / + 30 days
  • Stop time between -30 days / + 30 days
"},{"location":"usage/deduplication/#observables","title":"Observables","text":"

For STIX Cyber Observables, OpenCTI also generate deterministic IDs based on the STIX specification using the \"ID Contributing Properties\" defined for each type of observable.

"},{"location":"usage/deduplication/#update-behavior","title":"Update behavior","text":"

In cases where an entity already exists in the platform, incoming creations can trigger updates to the existing entity's attributes. This logic has been implemented to converge the knowledge base towards the highest confidence and quality levels for both entities and relationships.

To understand in details how the deduplication mechanism works in context of the maximum confidence level, you can navigate through this diagram (section deduplication):

"},{"location":"usage/delete-restore/","title":"Delete and restore knowledge","text":"

Knowledge can be deleted from OpenCTI either in an overview of an object or using background tasks. When an object is deleted, all its relationships and references to other objects are also deleted.

The deletion event is written to the stream, to trigger automated playbooks or synchronize another platform.

Since OpenCTI 6.1, a record of the deleted objects is kept for a given period of a time, allowing to restore them on demand. This does not impact the stream events or other side effect of the deletion: the object is still deleted.

"},{"location":"usage/delete-restore/#trash","title":"Trash","text":"

A view called \"Trash\" displays all \"delete\" operations, entities and relationships alike.

A delete operation contains not only the entity or relationship that has been deleted, but also all the relationships and references from (to) this main object to (from) other elements in the platform.

You can sort, filter or search this table using the usual UI controls. You are limited to the type of object, their representation (most of the time, the name of the object), the user who deleted the object, the date and time of deletion and the marking of the object.

Note that the delete operations (i.e. the entries in this table view) inherit the marking of the main entity that was deleted, and thus follow the same access restriction as the object that was deleted.

You can individually restore or permanently delete an object from the trash view using the burger menu at the end of the line.

Alternatively, you can use the checkboxes at the start of the line to select a subset of deleted objects, and trigger a background task to restore or permanently delete them by batch.

"},{"location":"usage/delete-restore/#restore","title":"Restore","text":"

Restoring an element creates it again in the platform with the same information it had before its deletion. It also restores all the relationships from or to this object, that have been also deleted during the deletion operation. If the object had attached files (uploaded or exported), they are also restored.

"},{"location":"usage/delete-restore/#permanent-delete","title":"Permanent delete","text":"

From the Trash panel, it is also possible to delete permanently the object, its relationships, and attached files.

"},{"location":"usage/delete-restore/#trash-retention","title":"Trash retention","text":"

Deleted objects are kept in trash during a fixed period of time (7 days by default), then they are permanently deleted by the trash manager.

"},{"location":"usage/delete-restore/#limitations","title":"Limitations","text":"

When it comes to restoring a deleted object from the trash, the current implementation shows several limitations. First and foremost, if an object in the trash has lost a relationship dependency (i.e. the other side of a relationship from or to this object is no longer in live database), you will not be able to restore the object.

In such case, if the missing dependency is in the trash too, you can manually restore this dependency first and then retry.

If the missing dependency has been permanently deleted, the object cannot be recovered.

In other words: * no partial restore: the object and all its relationships must be restored in one pass * no \"cascading\" restore: restoring one object does not restore automatically all linked objects in the trash

"},{"location":"usage/enrichment/","title":"Enrichment connectors","text":"

Enriching the data within the OpenCTI platform is made seamlessly through the integration of enrichment connectors. These connectors facilitate the retrieval of additional data from external sources or portals.

"},{"location":"usage/enrichment/#automatic-enrichment","title":"Automatic enrichment","text":"

Enrichment can be conducted automatically in two distinct modes:

  • Upon data arrival: Configuring the connector to run automatically when data arrives in OpenCTI ensures a real-time enrichment process, supplementing the platform's data. However, it's advisable to avoid automatic enrichment for quota-based connectors to paid sources to prevent quickly depleting all quotas. Additionally, this automatic enrichment contributes to increased data volume. On a large scale, with hundreds of thousands of objects, the disk space occupied by this data can be substantial, and it should be considered, especially if disk space is a concern. The automatic execution is configured at the connector level using the \"auto: true|false\" parameter.
  • Targeted enrichment via playbooks: Enrichment can also be performed in a more targeted manner using playbooks. This approach allows for a customized enrichment strategy, focusing on specific objects and optimizing the relevance of the retrieved data.
"},{"location":"usage/enrichment/#manual-enrichment","title":"Manual enrichment","text":"

Manually initiating the enrichment process is straightforward. Simply locate the button with the cloud icon at the top right of an entity.

Clicking on this icon unveils a side panel displaying a list of available connectors that can be activated for the given object. If no connectors appear in the panel, it indicates that no enrichment connectors are available for the specific type of object in focus.

Activation of an enrichment connector triggers a contact with the designated remote source, importing a set of data into OpenCTI to enrich the selected object. Each enrichment connector operates uniquely, focusing on a specific set of object types it can enrich and a distinct set of data it imports. Depending on the connectors, they may, establish relationships, add external references, or complete object information, thereby contributing to the comprehensiveness of information within the platform.

The list of available connectors can be found in our connectors catalog. In addition, further documentation on connectors is available on the dedicated documentation page.

Impact of the max confidence level

The maximum confidence level per user can have an impact on enrichment connectors, not being able to update data in the platform. To understand the concept and the potential issues you could face, please navigate to this page to understand.

"},{"location":"usage/exploring-analysis/","title":"Analyses","text":"

When you click on \"Analyses\" in the left-side bar, you see all the \"Analyses\" tabs, visible on the top bar on the left. By default, the user directly access the \"Reports\" tab, but can navigate to the other tabs as well.

From the Analyses section, users can access the following tabs:

  • Reports: See Reports as a sort of containers to detail and structure what is contained on a specific report, either from a source or write by yourself. Think of it as an Intelligence Production in OpenCTI.
  • Groupings: Groupings are containers, like Reports, but do not represent an Intelligence Production. They regroup Objects sharing an explicit context. For example, a Grouping might represent a set of data that, in time, given sufficient analysis, would mature to convey an incident or threat report as Report container.
  • Malware Analyses: As define by STIX 2.1 standard, Malware Analyses captures the metadata and results of a particular static or dynamic analysis performed on a malware instance or family.
  • Notes: Through this tab, you can find all the Notes that have been written in the platform, for example to add some analyst's unstructured knowledge about an Object.
  • External references: Intelligence is never created from nothing. External references give user a way to link sources or reference documents to any Object in the platform.

"},{"location":"usage/exploring-analysis/#reports","title":"Reports","text":""},{"location":"usage/exploring-analysis/#general-presentation","title":"General presentation","text":"

Reports are one of the central component of the platform. It is from a Report that knowledge is extracted and integrated in the platform for further navigation, analyses and exports. Always tying the information back to a report allows for the user to be able to identify the source of any piece of information in the platform at all time.

In the MITRE STIX 2.1 documentation, a Report is defined as such :

Reports are collections of threat intelligence focused on one or more topics, such as a description of a threat actor, malware, or attack technique, including context and related details. They are used to group related threat intelligence together so that it can be published as a comprehensive cyber threat story.

As a result, a Report object in OpenCTI is a set of attributes and metadata defining and describing a document outside the platform, which can be a threat intelligence report from a security reseearch team, a blog post, a press article a video, a conference extract, a MISP event, or any type of document and source.

When clicking on the Reports tab at the top left, you see the list of all the Reports you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of reports.

"},{"location":"usage/exploring-analysis/#visualizing-knowledge-within-a-report","title":"Visualizing Knowledge within a Report","text":"

When clicking on a Report, you land on the Overview tab. For a Report, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge contained in the report, accessible through different views (See below for a dive-in). As described here.
  • Content: a tab to upload or creates outcomes document displaying the content of the Report (for example in PDF, text, HTML or markdown files). The Content of the document is displayed to ease the access of Knowledge through a readable format. As described here.
  • Entities: A table containing all SDO (Stix Domain Objects) contained in the Report, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • Observables: A table containing all SCO (Stix Cyber Observable) contained in the Report, with search and filters available. It also displays if the SCO has been added directly or through inferences with the reasoning engine
  • Data: as described here.

Exploring and modifying the structured Knowledge contained in a Report can be done through different lenses.

"},{"location":"usage/exploring-analysis/#graph-view","title":"Graph View","text":"

In Graph view, STIX SDO are displayed as graph nodes and relationships as graph links. Nodes are colored depending of their type. Direct relationship are displayed as plain link and inferred relationships in dotted link. At the top right, you will find a serie of icons. From there you can change the current type of view. Here you can also perform global action on the Knowledge of the Report. Let's highlight 2 of them: - Suggestions: This tool suggests you some logical relationships to add between your contained Object to give more consistency to your Knowledge. - Share with an Organization: if you have designated a main Organization in the platform settings, you can here share your Report and its content with users of an other Organization. At the bottom, you have many option to manipulate the graph: - Multiple option for shaping the graph and applying forces to the nodes and links - Multiple selection options - Multiple filters, including a time range selector allowing you to see the evolution of the Knowledge within the Report. - Multiple creation and edition tools to modify the Knowledge contained in the Report.

"},{"location":"usage/exploring-analysis/#content-mapping-view","title":"Content mapping view","text":"

Through this view, you can map exsisting or new Objects directly from a readable content, allowing you to quickly append structured Knowledge in your Report before refining it with relationships and details. This view is a great place to see the continuum between unstructured and structured Knowledge of a specific Intelligence Production.

"},{"location":"usage/exploring-analysis/#timeline-view","title":"Timeline view","text":"

This view allows you to see the structured Knowledge chronologically. This view is really useful when the report describes an attack or a campaign that lasted some time, and the analyst payed attention to the dates. The view can be filtered and displayed relationships too.

"},{"location":"usage/exploring-analysis/#correlation-view","title":"Correlation view","text":"

The correlation view is a great way to visualize and find other Reports related to your current subject of interest. This graph displays all Report related to the important nodes contained in your current Report, for example Objects like Malware or Intrusion sets.

"},{"location":"usage/exploring-analysis/#matrix-view","title":"Matrix view","text":"

If your Report describes let's say an attack, a campaign, or an understanding of an Intrusion set, it should contains multiple attack patterns Objects to structure the Knowledge about the TTPs of the Threat Actor. Those attack patterns can be displayed as highlighted matrices, by default the MITRE ATT&CK Enterprise matrix. As some matrices can be huge, it can be also filtered to only display attack patterns describes in the Report.

"},{"location":"usage/exploring-analysis/#groupings","title":"Groupings","text":"

Groupings are an alternative to Report for grouping Objects sharing a context without describing an Intelligence Production.

In the MITRE STIX 2.1 documentation, a Grouping is defined as such :

A Grouping object explicitly asserts that the referenced STIX Objects have a shared context, unlike a STIX Bundle (which explicitly conveys no context). A Grouping object should not be confused with an intelligence product, which should be conveyed via a STIX Report. A STIX Grouping object might represent a set of data that, in time, given sufficient analysis, would mature to convey an incident or threat report as a STIX Report object. For example, a Grouping could be used to characterize an ongoing investigation into a security event or incident. A Grouping object could also be used to assert that the referenced STIX Objects are related to an ongoing analysis process, such as when a threat analyst is collaborating with others in their trust community to examine a series of Campaigns and Indicators.

When clicking on the Groupings tab at the top of the interface, you see the list of all the Groupings you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the groupings.

Clicking on a Grouping, you land on its Overview tab. For a Groupings, the following tabs are accessible: - Overview: as described here. - Knowledge: a complex tab that regroups all the structured Knowledge contained in the groupings, as for a Report, except for the Timeline view. As described here. - Entities: A table containing all SDO (Stix Domain Objects) contained in the Grouping, with search and filters available. It also display if the SDO has been added directly or through inferences with the reasonging engine - Observables: A table containing all SCO (Stix Cyber Observable) contained in the Grouping, with search and filters available. It also display if the SDO has been added directly or through inferences with the reasonging engine - Data: as described here.

"},{"location":"usage/exploring-analysis/#malware-analyses","title":"Malware Analyses","text":"

Malware analyses are an important part of the Cyber Threat Intelligence, allowing an precise understanding of what and how a malware really do on the host but also how and from where it receives its command and communicates its results.

In OpenCTI, Malware Analyses can be created from enrichment connectors that will take an Observable as input and perform a scan on a online service platform to bring back results. As such, Malware Analyses can be done on File, Domain and URL.

In the MITRE STIX 2.1 documentation, a Malware Analyses is defined as such :

Malware Analyses captures the metadata and results of a particular static or dynamic analysis performed on a malware instance or family.

When clicking on the Malware Analyses tab at the top of the interface, you see the list of all the Malware Analyses you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the Malware Analyses.

Clicking on a Malware Analyses, you land on its Overview tab. The following tabs are accessible: - Overview: This view contains some additions from the common Overview here. You will find here details about how the analysis have been performed, what is the global result regarding the malicioussness of the analysed artifact and all the Observables that have been found during the analysis. - Knowledge: If you Malware analysis is linked to other Objects that are not part of the analysis result, they will be displayed here. As described here. - Data: as described here. - History: as described here.

"},{"location":"usage/exploring-analysis/#notes","title":"Notes","text":"

Not every Knowledge can be structured. For allowing any users to share their insights about a specific Knowledge, they can create a Note for every Object and relationship in OpenCTI they can access to. All the Notes are listed within the Analyses menu for allowing global review of this unstructured addition to the global Knowledge.

In the MITRE STIX 2.1 documentation, a Note is defined as such :

A Note is intended to convey informative text to provide further context and/or to provide additional analysis not contained in the STIX Objects, Marking Definition objects, or Language Content objects which the Note relates to. Notes can be created by anyone (not just the original object creator).

Clicking on a Note, you land on its Overview tab. The following tabs are accessible: - Overview: as described here. - Data: as described here. - History: as described here.

"},{"location":"usage/exploring-analysis/#external-references","title":"External references","text":"

Intelligence is never created from nothing. External references give user a way to link sources or reference documents to any Object in the platform. All external references are listed within the Analyses menu for accessing directly sources of the structured Knowledge.

In the MITRE STIX 2.1 documentation, a External references is defined as such :

External references are used to describe pointers to information represented outside of STIX. For example, a Malware object could use an external reference to indicate an ID for that malware in an external database or a report could use references to represent source material.

Clicking on an External reference, you land on its Overview tab. The following tabs are accessible: - Overview: as described here.

"},{"location":"usage/exploring-arsenal/","title":"Arsenal","text":"

When you click on \"Arsenal\" in the left-side bar, you access all the \"Arsenal\" tabs, visible on the top bar on the left. By default, the user directly access the \"Malware\" tab, but can navigate to the other tabs as well.

From the Arsenal section, users can access the following tabs:

  • Malware: Malware represents any piece of code specifically designed to damage, disrupt, or gain unauthorized access to computer systems, networks, or user data.
  • Channels: Channels, in the context of cybersecurity, refer to places or means through which actors disseminate information. This category is used in particular in the context of FIMI (Foreign Information Manipulation Interference).
  • Tools: Tools represent legitimate, installed software or hardware applications on an operating system that can be misused by attackers for malicious purposes. (e.g. LOLBAS).
  • Vulnerabilities: Vulnerabilities are weaknesses or that can be exploited by attackers to compromise the security, integrity, or availability of a computer system or network.
"},{"location":"usage/exploring-arsenal/#malware","title":"Malware","text":""},{"location":"usage/exploring-arsenal/#general-presentation","title":"General presentation","text":"

Malware encompasses a broad category of malicious pieces of code built, deployed, and operated by intrusion set. Malware can take many forms, including viruses, worms, Trojans, ransomware, spyware, and more. These entities are created by individuals or groups, including state-nations, state-sponsored groups, corporations, or hacktivist collectives.

Use the Malware SDO to model and track these threats comprehensively, facilitating in-depth analysis, response, and correlation with other security data.

When clicking on the Malware tab on the top left, you see the list of all the Malware you have access to, in respect with your allowed marking definitions. These malware are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, related intrusion sets, countries and sectors they target, and labels. You can then search and filter on some common and specific attributes of Malware.

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

"},{"location":"usage/exploring-arsenal/#visualizing-knowledge-associated-with-a-malware","title":"Visualizing Knowledge associated with a Malware","text":"

When clicking on an Malware card you land on its Overview tab. For a Malware, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Malware. Different thematic views are proposed to easily see the victimology, the threat actors and intrusion sets using the Malware, etc. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-arsenal/#channels","title":"Channels","text":""},{"location":"usage/exploring-arsenal/#general-presentation_1","title":"General presentation","text":"

Channels - such as forums, websites and social media platforms (e.g. Twitter, Telegram) - are mediums for disseminating news, knowledge, and messages to a broad audience. While they offer benefits like open communication and outreach, they can also be leveraged for nefarious purposes, such as spreading misinformation, coordinating cyberattacks, or promoting illegal activities.

Monitoring and managing content within Channels aids in analyzing threats, activities, and indicators associated with various threat actors, campaigns, and intrusion sets.

When clicking on the Channels tab at the top left, you see the list of all the Channels you have access to, in respect with your allowed marking definitions. These channels are displayed in a list where you can find certain fields characterizing the entity: type of channel, labels, and dates. You can then search and filter on some common and specific attributes of Channels.

"},{"location":"usage/exploring-arsenal/#visualizing-knowledge-associated-with-a-channel","title":"Visualizing Knowledge associated with a Channel","text":"

When clicking on a Channel in the list, you land on its Overview tab. For a Channel, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Channel. Different thematic views are proposed to easily see the victimology, the threat actors and intrusion sets using the Malware, etc. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-arsenal/#tools","title":"Tools","text":""},{"location":"usage/exploring-arsenal/#general-presentation_2","title":"General presentation","text":"

Tools refers to legitimate, pre-installed software applications, command-line utilities, or scripts that are present on a compromised system. These objects enable you to model and monitor the activities of these tools, which can be misused by attackers.

When clicking on the Tools tab at the top left, you see the list of all the Tools you have access to, in respect with your allowed marking definitions. These tools are displayed in a list where you can find certain fields characterizing the entity: labels and dates. You can then search and filter on some common and specific attributes of Tools.

"},{"location":"usage/exploring-arsenal/#visualizing-knowledge-associated-with-an-observed-data","title":"Visualizing Knowledge associated with an Observed Data","text":"

When clicking on a Tool in the list, you land on its Overview tab. For a Tool, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Tool. Different thematic views are proposed to easily see the threat actors, the intrusion sets and the malware using the Tool. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-arsenal/#vulnerabilities","title":"Vulnerabilities","text":""},{"location":"usage/exploring-arsenal/#general-presentation_3","title":"General presentation","text":"

Vulnerabilities represent weaknesses or flaws in software, hardware, configurations, or systems that can be exploited by malicious actors. This object assists in managing and tracking the organization's security posture by identifying areas that require attention and remediation, while also providing insights into associated intrusion sets, malware and campaigns where relevant.

When clicking on the Vulnerabilities tab at the top left, you see the list of all the Vulnerabilities you have access to, in respect with your allowed marking definitions. These vulnerabilities are displayed in a list where you can find certain fields characterizing the entity: CVSS3 severity, labels, dates and creators (in the platform). You can then search and filter on some common and specific attributes of Vulnerabilities.

"},{"location":"usage/exploring-arsenal/#visualizing-knowledge-associated-with-an-observed-data_1","title":"Visualizing Knowledge associated with an Observed Data","text":"

When clicking on a Vulnerabilities in the list, you land on its Overview tab. For a Vulnerability, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Vulnerability. Different thematic views are proposed to easily see the threat actors, the intrusion sets and the malware exploiting the Vulnerability. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-cases/","title":"Cases","text":"

When you click on \"Cases\" in the left-side bar, you access all the \"Cases\" tabs, visible on the top bar on the left. By default, the user directly access the \"Incident Responses\" tab, but can navigate to the other tabs as well.

As Analyses, Cases can contain other objects. This way, by adding context and results of your investigations in the case, you will be able to get an up-to-date overview of the ongoing situation, and later produce more easily an incident report.

From the Cases section, users can access the following tabs:

  • Incident Responses: This type of Cases is dedicated to the management of incidents. An Incident Response case does not represent an incident, but all the context and actions that will encompass the response to a specific incident.
  • Request for Information: CTI teams are often asked to provide extensive information and analysis on a specific subject, be it related to an ongoing incident or a particular trending threat. Request for Information cases allow you to store context and actions relative to this type of request and its response.
  • Request for Takedown: When an organization is targeted by an attack campaign, a typical response action can be to request the Takedown of elements of the attack infrastructure, for example a domain name impersonating the organization to phish its employees, or an email address used to deliver phishing content. As Takedown needs in most case to reach out to external providers and be effective quickly, it often needs specific workflows. Request for Takedown cases give you a dedicated space to manage these specific actions.
  • Tasks: In every case, you need tasks to be performed in order to solve it. The Tasks tab allows you to review all created tasks to quickly see past due date, or quickly see every task assigned to a specific user.
  • Feedbacks: If you use your platform to interact with other teams and provide them CTI Knowledge, some users may want to give you feedback about it. Those feedbacks can easily be considered as another type of case to solve, as it will often refer to Knowledge inconsistency or gaps.

"},{"location":"usage/exploring-cases/#incident-response-request-for-information-request-for-takedown","title":"Incident Response, Request for Information & Request for Takedown","text":""},{"location":"usage/exploring-cases/#general-presentation","title":"General presentation","text":"

Incident responses, Request for Information & Request for Takedown cases are an important part of the case management system in OpenCTI. Here, you can organize the work of your team to respond to cybersecurity situations. You can also give context to the team and other users on the platform about the situation and actions (to be) taken.

To manage the situation, you can issue Tasks and assign them to users in the platform, by directly creating a Task or by applying a Case template that will append a list of predefined tasks.

To bring context, you can use your Case as a container (like Reports or Groupings), allowing you to add any Knowledge from your platform in it. You can also use this possibility to trace your investigation, your Case playing the role of an Incident report. You will find more information about case management here.

Incident Response, Request for Information & Request for Takedown are not STIX 2.1 Objects.

When clicking on the Incident Response, Request for Information & Request for Takedown tabs at the top, you see the list of all the Cases you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes.

"},{"location":"usage/exploring-cases/#visualizing-knowledge-within-an-incident-response-request-for-information-request-for-takedown","title":"Visualizing Knowledge within an Incident Response, Request for Information & Request for Takedown","text":"

When clicking on an Incident Response, Request for Information or Request for Takedown, you land on the Overview tab. The following tabs are accessible:

  • Overview: Overview of Cases are slightly different from the usual (described here). Cases' Overview displays also the list of the tasks associated with the case. It also let you highlight Incident, Report or Sighting at the origin of the case. If other cases contains some Observables with your Case, they will be displayed as Related Cases in the Overview.
  • Knowledge: a complex tab that regroups all the structured Knowledge contained in the Case, accessible through different views (See below for a dive-in). As described here.
  • Content: a tab to upload or creates outcomes document displaying the content of the Case (for example in PDF, text, HTML or markdown files). The Content of the document is displayed to ease the access of Knowledge through a readable format. As described here.
  • Entities: A table containing all SDO (Stix Domain Objects) contained in the Case, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • Observables: A table containing all SCO (Stix Cyber Observable) contained in the Case, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • Data: as described here.

Exploring and modifying the structured Knowledge contained in a Case can be done through different lenses.

"},{"location":"usage/exploring-cases/#graph-view","title":"Graph View","text":"

In Graph view, STIX SDO are displayed as graph nodes and relationships as graph links. Nodes are colored depending on their type. Direct relationship are displayed as plain link and inferred relationships in dotted link. At the top right, you will find a series of icons. From there you can change the current type of view. Here you can also perform global action on the Knowledge of the Case. Let's highlight 2 of them:

  • Suggestions: This tool suggests you some logical relationships to add between your contained Object to give more consistency to your Knowledge.
  • Share with an Organization: if you have designated a main Organization in the platform settings, you can here share your Case and its content with users of another Organization. At the bottom, you have many option to manipulate the graph:
  • Multiple option for shaping the graph and applying forces to the nodes and links
  • Multiple selection options
  • Multiple filters, including a time range selector allowing you to see the evolution of the Knowledge within the Case.
  • Multiple creation and edition tools to modify the Knowledge contained in the Case.
"},{"location":"usage/exploring-cases/#content-mapping-view","title":"Content mapping view","text":"

Through this view, you can map existing or new Objects directly from a readable content, allowing you to quickly append structured Knowledge in your Case before refining it with relationships and details. This view is a great place to see the continuum between unstructured and structured Knowledge.

"},{"location":"usage/exploring-cases/#timeline-view","title":"Timeline view","text":"

This view allows you to see the structured Knowledge chronologically. This view is particularly useful in the context of a Case, allowing you to see the chain of events, either from the attack perspectives, the defense perspectives or both. The view can be filtered and displayed relationships too.

"},{"location":"usage/exploring-cases/#matrix-view","title":"Matrix view","text":"

If your Case contains attack patterns, you will be able to visualize them in a Matrix view.

"},{"location":"usage/exploring-cases/#tasks","title":"Tasks","text":"

Tasks are actions to be performed in the context of a Case (Incident Response, Request for Information, Request for Takedown). Usually, a task is assigned to a user, but important tasks may involve more participants.

When clicking on the Tasks tab at the top of the interface, you see the list of all the Tasks you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the tasks.

Clicking on a Task, you land on its Overview tab. For a Tasks, the following tabs are accessible:

  • Overview: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-cases/#feedbacks","title":"Feedbacks","text":"

When a user fill a feedback form from its Profile/Feedback menu, it will then be accessible here.

This feature gives the opportunity to engage with other users of your platform and to respond directly to their concern about it or the Knowledge, without the need of third party software.

Clicking on a Feedback, you land on its Overview tab. For a Feedback, the following tabs are accessible:

  • Overview: as described here.
  • Content: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-entities/","title":"Entities","text":"

OpenCTI's Entities objects provides a comprehensive framework for modeling various targets and attack victims within your threat intelligence data. With five distinct Entity object types, you can represent sectors, events, organizations, systems, and individuals. This robust classification empowers you to contextualize threats effectively, enhancing the depth and precision of your analysis.

When you click on \"Entities\" in the left-side bar, you access all the \"Entities\" tabs, visible on the top bar on the left. By default, the user directly access the \"Sectors\" tab, but can navigate to the other tabs as well.

From the Entities section, users can access the following tabs:

  • Sectors: areas of activity.
  • Events: event in the real world.
  • Organizations: groups with specific aims such as companies and government entities.
  • Systems: technologies such as platforms and software.
  • Individuals: real persons.
"},{"location":"usage/exploring-entities/#sectors","title":"Sectors","text":""},{"location":"usage/exploring-entities/#general-presentation","title":"General presentation","text":"

Sectors represent specific domains of activity, defining areas such as energy, government, health, finance, and more. Utilize sectors to categorize targeted industries or sectors of interest, providing valuable context for threat intelligence analysis within distinct areas of the economy.

When clicking on the Sectors tab at the top left, you see the list of all the Sectors you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-entities/#visualizing-knowledge-associated-with-a-sector","title":"Visualizing Knowledge associated with a Sector","text":"

When clicking on a Sector in the list, you land on its Overview tab. For a Sector, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Sector. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Sector. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Sector.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-entities/#events","title":"Events","text":""},{"location":"usage/exploring-entities/#general-presentation_1","title":"General presentation","text":"

Events encompass occurrences like international sports events, summits (e.g., G20), trials, conferences, or any significant happening in the real world. By modeling events, you can analyze threats associated with specific occurrences, allowing for targeted investigations surrounding high-profile incidents.

When clicking on the Events tab at the top left, you see the list of all the Events you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-entities/#visualizing-knowledge-associated-with-an-event","title":"Visualizing Knowledge associated with an Event","text":"

When clicking on an Event in the list, you land on its Overview tab. For an Event, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Event. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Event. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted during an attack against the Event.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-entities/#organizations","title":"Organizations","text":""},{"location":"usage/exploring-entities/#general-presentation_2","title":"General presentation","text":"

Organizations include diverse entities such as companies, government bodies, associations, non-profits, and other groups with specific aims. Modeling organizations enables you to understand the threat landscape concerning various entities, facilitating investigations into cyber-espionage, data breaches, or other malicious activities targeting specific groups.

When clicking on the Organizations tab at the top left, you see the list of all the Organizations you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-entities/#visualizing-knowledge-associated-with-an-organization","title":"Visualizing Knowledge associated with an Organization","text":"

When clicking on an Organization in the list, you land on its Overview tab. For an Organization, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Organization. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Organization. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Organization.
  • Data: as described here.
  • History: as described here.

Furthermore, an Organization can be observed from an \"Author\" perspective. It is possible to change this viewpoint to the right of the entity name, using the \"Display as\" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to \"Author\" mode, the observed data pertains to the entity's description as an author within the platform:

  • Overview: The \"Latest created relationships\" and \"Latest containers about the object\" panels are replaced by the \"Latest containers authored by this entity\" panel.
  • Knowledge: A tab that presents an overview of the data authored by the Organization (i.e. counters and a graph).
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the Organization is the author.

"},{"location":"usage/exploring-entities/#systems","title":"Systems","text":""},{"location":"usage/exploring-entities/#general-presentation_3","title":"General presentation","text":"

Systems represent software applications, platforms, frameworks, or specific tools like WordPress, VirtualBox, Firefox, Python, etc. Modeling systems allows you to focus on threats related to specific software or technology, aiding in vulnerability assessments, patch management, and securing critical applications.

When clicking on the Systems tab at the top left, you see the list of all the Systems you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-entities/#visualizing-knowledge-associated-with-a-system","title":"Visualizing Knowledge associated with a System","text":"

When clicking on a System in the list, you land on its Overview tab. For a System, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the System. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the System. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the System.
  • Data: as described here.
  • History: as described here.

Furthermore, a System can be observed from an \"Author\" perspective. It is possible to change this viewpoint to the right of the entity name, using the \"Display as\" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to \"Author\" mode, the observed data pertains to the entity's description as an author within the platform:

  • Overview: The \"Latest created relationships\" and \"Latest containers about the object\" panels are replaced by the \"Latest containers authored by this entity\" panel.
  • Knowledge: A tab that presents an overview of the data authored by the System (i.e. counters and a graph).
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the System is the author.
"},{"location":"usage/exploring-entities/#individuals","title":"Individuals","text":""},{"location":"usage/exploring-entities/#general-presentation_4","title":"General presentation","text":"

Individuals represent specific persons relevant to your threat intelligence analysis. This category includes targeted individuals, or influential figures in various fields. Modeling individuals enables you to analyze threats related to specific people, enhancing investigations into cyber-stalking, impersonation, or other targeted attacks.

When clicking on the Individuals tab at the top left, you see the list of all the Individuals you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-entities/#visualizing-knowledge-associated-with-an-individual","title":"Visualizing Knowledge associated with an Individual","text":"

When clicking on an Individual in the list, you land on its Overview tab. For an Individual, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Individual. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Individual. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Individual.
  • Data: as described here.
  • History: as described here.

Furthermore, an Individual can be observed from an \"Author\" perspective. It is possible to change this viewpoint to the right of the entity name, using the \"Display as\" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to \"Author\" mode, the observed data pertains to the entity's description as an author within the platform:

  • Overview: The \"Latest created relationships\" and \"Latest containers about the object\" panels are replaced by the \"Latest containers authored by this entity\" panel.
  • Knowledge: A tab that presents an overview of the data authored by the Individual (i.e. counters and a graph).
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the Individual is the author.
"},{"location":"usage/exploring-events/","title":"Events","text":"

When you click on \"Events\" in the left-side bar, you access all the \"Events\" tabs, visible on the top bar on the left. By default, the user directly access the \"Incidents\" tab, but can navigate to the other tabs as well.

From the Events section, users can access the following tabs:

  • Incidents: In OpenCTI, Incidents correspond to a negative event happening on an information system. This can include a cyberattack (intrusion, phishing, etc.), a consolidated security alert generated by a SIEM or EDR that need to be qualified, and so on. It can also refer to an information warfare attack in the context of countering disinformation.
  • Sightings: Sightings correspond to the event in which an Observable (IP, domain name, certificate, etc.) is detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or an EDR.
  • Observed Data: Observed Data has been added in OpenCTI by compliance with the STIX 2.1 standard. You can see it has a pseudo-container that contains Observables, like a line of firewall log for example. Currently, it is rarely used.
"},{"location":"usage/exploring-events/#incidents","title":"Incidents","text":""},{"location":"usage/exploring-events/#general-presentation","title":"General presentation","text":"

Incidents usually represents negative events impacting resources you want to protect, but local definitions can vary a lot, from a simple security events send by a SIEM to a massive scale supply chain attack impacting a whole activity sector.

In the MITRE STIX 2.1, the Incident SDO has not yet been finalized and is the object of important work as part of a forthcoming STIX Extension.

When clicking on the Incidents tab at the top left, you see the list of all the Incidents you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-events/#visualizing-knowledge-associated-with-an-incident","title":"Visualizing Knowledge associated with an Incident","text":"

When clicking on an Incident in the list, you land on its Overview tab. For an Incident, the following tabs are accessible:

  • Overview: as described here, with the particularity to display two distribution graphs of its related Entities (STIX SDO) and Observable (STIX SCO).
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Incident. Different thematic views are proposed to easily see the victimology, arsenal, techniques and so on used in the context of the Incident. As described here.
  • Content: This specific tab allows to previzualize, manage and write deliverable associated with the Incident. For example an analytic report to share with other teams, a markdown files to feed a collaborative wiki with, etc. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-events/#sightings","title":"Sightings","text":""},{"location":"usage/exploring-events/#general-presentation_1","title":"General presentation","text":"

The Sightings correspond to events in which an Observable (IP, domain name, url, etc.) is detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or EDR.

In OpenCTI, as we are in a cybersecurity context, Sightings are associated with Indicators of Compromise (IoC) and the notion of \"True positive\" and \"False positive\".

It is important to note that Sightings are a type of relationship (not a STIX SDO or STIX SCO), between an Observable and an Entities or Locations.

When clicking on the Sightings tab at the top left, you see the list of all the Sightings you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-events/#visualizing-knowledge-associated-with-a-sighting","title":"Visualizing Knowledge associated with a Sighting","text":"

When clicking on a Sighting in the list, you land on its Overview tab. As other relationships in the platform, Sighting's overview displays common related metadata, containers, external references, notes and entities linked by the relationship.

In addition, this overview displays: - Qualification : if the Sighting is a True Positive or a False Positive - Count : number of times the event has been seen

"},{"location":"usage/exploring-events/#observed-data","title":"Observed Data","text":""},{"location":"usage/exploring-events/#general-presentation_2","title":"General presentation","text":"

Observed Data correspond to an extract from a log that contains Observables.

In the MITRE STIX 2.1, the Observed Data SDO is defined as such:

Observed Data conveys information about cybersecurity related entities such as files, systems, and networks using the STIX Cyber-observable Objects (SCOs). For example, Observed Data can capture information about an IP address, a network connection, a file, or a registry key. Observed Data is not an intelligence assertion, it is simply the raw information without any context for what it means.

When clicking on the Observed Data tab at the top left, you see the list of all the Observed Data you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-events/#visualizing-knowledge-associated-with-an-observed-data","title":"Visualizing Knowledge associated with an Observed Data","text":"

When clicking on an Observed Data in the list, you land on its Overview tab. The following tabs are accessible:

  • Overview: as described here, with the particularity to display a distribution graphs of its related Observables (STIX SCO).
  • Entities : a sortable and filterable list of all Entities (SDO)
  • Observables: a sortable and filterable list of all Observables (SCO) in relation with the Observed Data
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-locations/","title":"Location","text":"

OpenCTI's Locations objects provides a comprehensive framework for representing various geographic entities within your threat intelligence data. With five distinct Location object types, you can precisely define regions, countries, areas, cities, and specific positions. This robust classification empowers you to contextualize threats geographically, enhancing the depth and accuracy of your analysis.

When you click on \"Locations\" in the left-side bar, you access all the \"Locations\" tabs, visible on the top bar on the left. By default, the user directly access the \"Regions\" tab, but can navigate to the other tabs as well.

From the Locations section, users can access the following tabs:

  • Regions: very large geographical territories, such as a continent.
  • Countries: the world's countries.
  • Areas: more or less extensive geographical areas and often not having a very defined limit
  • Cities: the world's cities.
  • Positions: very precise positions on the globe.
"},{"location":"usage/exploring-locations/#regions","title":"Regions","text":""},{"location":"usage/exploring-locations/#general-presentation","title":"General presentation","text":"

Regions encapsulate broader geographical territories, often representing continents or significant parts of continents. Examples include EMEA (Europe, Middle East, and Africa), Asia, Western Europe, and North America. Utilize regions to categorize large geopolitical areas and gain macro-level insights into threat patterns.

When clicking on the Regions tab at the top left, you see the list of all the Regions you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-locations/#visualizing-knowledge-associated-with-a-region","title":"Visualizing Knowledge associated with a Region","text":"

When clicking on a Region in the list, you land on its Overview tab. For a Region, the following tabs are accessible:

  • Overview: as described here, with the particularity of not having a Details section but a map locating the Region.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Region. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Region. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a Region.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-locations/#countries","title":"Countries","text":""},{"location":"usage/exploring-locations/#general-presentation_1","title":"General presentation","text":"

Countries represent individual nations across the world. With this object type, you can specify detailed information about a particular country, enabling precise localization of threat intelligence data. Countries are fundamental entities in geopolitical analysis, offering a focused view of activities within national borders.

When clicking on the Countries tab at the top left, you see the list of all the Countries you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-locations/#visualizing-knowledge-associated-with-a-country","title":"Visualizing Knowledge associated with a Country","text":"

When clicking on a Country in the list, you land on its Overview tab. For a Country, the following tabs are accessible:

  • Overview: as described here, with the particularity of not having a Details section but a map locating the Country.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Country. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Country. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a Country.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-locations/#areas","title":"Areas","text":""},{"location":"usage/exploring-locations/#general-presentation_2","title":"General presentation","text":"

Areas define specific geographical regions of interest, such as the Persian Gulf, the Balkans, or the Caucasus. Use areas to identify unique zones with distinct geopolitical, cultural, or strategic significance. This object type facilitates nuanced analysis of threats within defined geographic contexts.

When clicking on the Areas tab at the top left, you see the list of all the Areas you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-locations/#visualizing-knowledge-associated-with-an-area","title":"Visualizing Knowledge associated with an Area","text":"

When clicking on an Area in the list, you land on its Overview tab. For an Area, the following tabs are accessible:

  • Overview: as described here, with the particularity of not having a Details section but a map locating the Area.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Area. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Area. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in an Area.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-locations/#cities","title":"Cities","text":""},{"location":"usage/exploring-locations/#general-presentation_3","title":"General presentation","text":"

Cities provide granular information about urban centers worldwide. From major metropolises to smaller towns, cities are crucial in understanding localized threat activities. With this object type, you can pinpoint threats at the urban level, aiding in tactical threat assessments and response planning.

When clicking on the Cities tab at the top left, you see the list of all the Cities you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-locations/#visualizing-knowledge-associated-with-a-city","title":"Visualizing Knowledge associated with a City","text":"

When clicking on a City in the list, you land on its Overview tab. For a City, the following tabs are accessible:

  • Overview: as described here, with the particularity of not having a Details section but a map locating the City.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the City. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the City. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a City.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-locations/#positions","title":"Positions","text":""},{"location":"usage/exploring-locations/#general-presentation_4","title":"General presentation","text":"

Positions represent highly precise geographical points, such as monuments, buildings, or specific event locations. This object type allows you to define exact coordinates, enabling accurate mapping of events or incidents. Positions enhance the granularity of your threat intelligence data, facilitating precise geospatial analysis.

When clicking on the Positions tab at the top left, you see the list of all the Positions you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-locations/#visualizing-knowledge-associated-with-a-position","title":"Visualizing Knowledge associated with a Position","text":"

When clicking on a Position in the list, you land on its Overview tab. For a Position, the following tabs are accessible:

  • Overview: as described here, with the particularity to display a map locating the Position.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Position. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Position. As described here.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted at a Position.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-observations/","title":"Observations","text":"

When you click on \"Observations\" in the left-side bar, you access all the \"Observations\" tabs, visible on the top bar on the left. By default, the user directly access the \"Observables\" tab, but can navigate to the other tabs as well.

From the Observations section, users can access the following tabs:

  • Observables: An Observable represents an immutable object. Observables can encompass a wide range of entities such as IPv4 addresses, domain names, email addresses, and more.
  • Artefacts: In OpenCTI, the Artefacts is a particular Observable. It may contain a file, such as a malware sample.
  • Indicators: An Indicator is a detection object. It is defined by a search pattern, which could be expressed in various formats such as STIX, Sigma, YARA, among others.
  • Infrastructures: An Infrastructure describes any systems, software services and any associated physical or virtual resources intended to support some purpose (e.g. C2 servers used as part of an attack, devices or servers that are part of defense, database servers targeted by an attack, etc.).
"},{"location":"usage/exploring-observations/#observables","title":"Observables","text":""},{"location":"usage/exploring-observations/#general-presentation","title":"General presentation","text":"

An Observable is a distinct entity from the Indicator within OpenCTI and represents an immutable object. Observables can encompass a wide range of entities such as IPv4 addresses, domain names, email addresses, and more. Importantly, Observables don't inherently imply malicious intent, they can include items like legitimate IP addresses or domains associated with an organization. Additionally, they serve as raw data points without the additional detection context found in Indicators.

When clicking on the Observables tab at the top left, you see the list of all the Observables you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-observations/#visualizing-knowledge-associated-with-an-observable","title":"Visualizing Knowledge associated with an Observable","text":"

When clicking on an Observable in the list, you land on its Overview tab. For an Observable, the following tabs are accessible:

  • Overview: as described here, with the particularity to display Indicators composed with the Observable.
  • Knowledge: a tab listing all its relationships and nested objects.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Observable (IP, domain name, url, etc.) has been sighted.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-observations/#artefacts","title":"Artefacts","text":""},{"location":"usage/exploring-observations/#general-presentation_1","title":"General presentation","text":"

An Artefact is a particular Observable. It may contain a file, such as a malware sample. Files can be uploaded or downloaded in encrypted archives, providing an additional layer of security against potential manipulation of malicious payloads.

When clicking on the Artefacts tab at the top left, you see the list of all the Artefacts you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-observations/#visualizing-knowledge-associated-with-an-artefact","title":"Visualizing Knowledge associated with an Artefact","text":"

When clicking on an Artefact in the list, you land on its Overview tab. For an Artefact, the following tabs are accessible:

  • Overview: as described here, with the particularity to be able to download the attached file.
  • Knowledge: a tab listing all its relationships and nested objects.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Artefact has been sighted.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-observations/#indicators","title":"Indicators","text":""},{"location":"usage/exploring-observations/#general-presentation_2","title":"General presentation","text":"

An Indicator is a detection object. It is defined by a search pattern, which could be expressed in various formats such as STIX, Sigma, YARA, among others. This pattern serves as a key to identify potential threats within the data. Furthermore, an Indicator includes additional information that enriches its detection context. This information encompasses:

  • Validity dates: Indicators are accompanied by a time frame, specifying the duration of their relevance, and modeled by the Valid from and Valid until dates.
  • Actionable fields: Linked to the validity dates, the Revoked and Detection fields can be used to sort Indicators for detection purposes.
  • Kill chain phase: They indicate the phase within the cyber kill chain where they are applicable, offering insights into the progression of a potential threat.
  • Types: Indicators are categorized based on their nature, aiding in classification and analysis.

When clicking on the Indicators tab at the top left, you see the list of all the Indicators you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-observations/#visualizing-knowledge-associated-with-an-indicator","title":"Visualizing Knowledge associated with an Indicator","text":"

When clicking on an Indicator in the list, you land on its Overview tab. For an Indicator, the following tabs are accessible:

  • Overview: as described here, with the particularity to display the Observables on which it is based.
  • Knowledge: a tab listing all its relationships.
  • Analyses: as described here.
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Indicator has been sighted.
  • Data: as described here.
  • History: as described here.

"},{"location":"usage/exploring-observations/#infrastructures","title":"Infrastructures","text":""},{"location":"usage/exploring-observations/#general-presentation_3","title":"General presentation","text":"

An Infrastructure refers to a set of resources, tools, systems, or services employed by a threat to conduct their activities. It represents the underlying framework or support system that facilitates malicious operations, such as the command and control (C2) servers in an attack. Notably, like Observables, an Infrastructure doesn't inherently imply malicious intent. It can also represent legitimate resources affiliated with an organization (e.g. devices or servers that are part of defense, database servers targeted by an attack, etc.).

When clicking on the Infrastructures tab at the top left, you see the list of all the Infrastructures you have access to, in respect with your allowed marking definitions.

"},{"location":"usage/exploring-observations/#visualizing-knowledge-associated-with-an-infrastructure","title":"Visualizing Knowledge associated with an Infrastructure","text":"

When clicking on an Infrastructure in the list, you land on its Overview tab. For an Infrastructure, the following tabs are accessible:

  • Overview: as described here, with the particularity to display distribution graphs of its related Observable (STIX SCO).
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Infrastructure. Different thematic views are proposed to easily see the threats, the arsenal, the observations, etc. linked to the Infrastructure. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-techniques/","title":"Techniques","text":"

When you click on \"Techniques\" in the left-side bar, you access all the \"Techniques\" tabs, visible on the top bar on the left. By default, the user directly access the \"Attack pattern\" tab, but can navigate to the other tabs as well.

From the Techniques section, users can access the following tabs:

  • Attack pattern: attacks pattern used by the threat actors to perform their attacks. By default, OpenCTI is provisionned with attack patterns from MITRE ATT&CK matrices (for CTI) and DISARM matrix (for FIMI).
  • Narratives: In OpenCTI, narratives used by threat actors can be represented and linked to other Objects. Narratives are mainly used in the context of disinformation campaigns where it is important to trace which narratives have been and are still used by threat actors.
  • Courses of action: A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress. It may describe technical, automatable responses (applying patches, reconfiguring firewalls) but can also describe higher level actions like employee training or policy changes. For example, a course of action to mitigate a vulnerability could describe applying the patch that fixes it.
  • Data sources: Data sources represent the various subjects/topics of information that can be collected by sensors/logs. Data sources also include data components,
  • Data components: Data components identify specific properties/values of a data source relevant to detecting a given ATT&CK technique or sub-technique.
"},{"location":"usage/exploring-techniques/#attack-pattern","title":"Attack pattern","text":""},{"location":"usage/exploring-techniques/#general-presentation","title":"General presentation","text":"

Attacks pattern used by the threat actors to perform their attacks. By default, OpenCTI is provisionned with attack patterns from MITRE ATT&CK matrices and CAPEC (for CTI) and DISARM matrix (for FIMI).

In the MITRE STIX 2.1 documentation, an Attack pattern is defined as such :

Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets. Attack Patterns are used to help categorize attacks, generalize specific attacks to the patterns that they follow, and provide detailed information about how attacks are performed. An example of an attack pattern is \"spear phishing\": a common type of attack where an attacker sends a carefully crafted e-mail message to a party with the intent of getting them to click a link or open an attachment to deliver malware. Attack Patterns can also be more specific; spear phishing as practiced by a particular threat actor (e.g., they might generally say that the target won a contest) can also be an Attack Pattern.

When clicking on the Attack pattern tab at the top left, you access the list of all the attack pattern you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of attack patterns.

"},{"location":"usage/exploring-techniques/#visualizing-knowledge-associated-with-an-attack-pattern","title":"Visualizing Knowledge associated with an Attack pattern","text":"

When clicking on an Attack pattern, you land on its Overview tab. For an Attack pattern, the following tabs are accessible:

  • Overview: Overview of Attack pattern is a bit different as the usual described here. The \"Details\" box is more structured and contains information about:

  • parent or subtechniques (as in the MITRE ATT&CK matrices),

  • related kill chain phases
  • Platform on which the Attack pattern is usable,
  • permission required to apply it
  • Related detection technique
  • Courses of action to mitigate the Attack pattern
  • Data components in which find data to detect the usage of the Attack pattern
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Attack pattern. Different thematic views are proposed to easily see Threat Actors and Intrusion Sets using this techniques, linked incidents, etc.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-techniques/#narratives","title":"Narratives","text":""},{"location":"usage/exploring-techniques/#general-presentation_1","title":"General presentation","text":"

In OpenCTI, narratives used by threat actors can be represented and linked to other Objects. Narratives are mainly used in the context of disinformation campaigns where it is important to trace which narratives have been and are still used by threat actors.

An example of Narrative can be \"The country A is weak and corrupted\" or \"The ongoing operation aims to free people\".

Narrative can be a mean in the context of a more broad attack or the goal of the operation, a vision to impose.

When clicking on the Narrative tab at the top left, you access the list of all the Narratives you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of narratives.

"},{"location":"usage/exploring-techniques/#visualizing-knowledge-associated-with-a-narrative","title":"Visualizing Knowledge associated with a Narrative","text":"

When clicking on a Narrative, you land on its Overview tab. For a Narrative, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Narratives. Different thematic views are proposed to easily see the Threat actors and Intrusion Set using the Narrative, etc.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-techniques/#courses-of-action","title":"Courses of action","text":""},{"location":"usage/exploring-techniques/#general-presentation_2","title":"General presentation","text":"

In the MITRE STIX 2.1 documentation, an Course of action is defined as such :

A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress. It may describe technical, automatable responses (applying patches, reconfiguring firewalls) but can also describe higher level actions like employee training or policy changes. For example, a course of action to mitigate a vulnerability could describe applying the patch that fixes it.

When clicking on the Courses of action tab at the top left, you access the list of all the Courses of action you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of course of action.

"},{"location":"usage/exploring-techniques/#visualizing-knowledge-associated-with-a-course-of-action","title":"Visualizing Knowledge associated with a Course of action","text":"

When clicking on a Course of Action, you land on its Overview tab. For a Course of action, the following tabs are accessible:

  • Overview: Overview of Course of action is a bit different as the usual described here. In \"Details\" box, mitigated attack pattern are listed.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Narratives. Different thematic views are proposed to easily see the Threat actors and Intrusion Set using the Narrative, etc.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-techniques/#data-sources-data-components","title":"Data sources & Data components","text":""},{"location":"usage/exploring-techniques/#general-presentation_3","title":"General presentation","text":"

In the MITRE ATT&CK documentation, Data sources are defined as such :

Data sources represent the various subjects/topics of information that can be collected by sensors/logs. Data sources also include data components, which identify specific properties/values of a data source relevant to detecting a given ATT&CK technique or sub-technique.

"},{"location":"usage/exploring-techniques/#visualizing-knowledge-associated-with-a-data-source-or-a-data-components","title":"Visualizing Knowledge associated with a Data source or a Data components","text":"

When clicking on a Data source or a Data component, you land on its Overview tab. For a Course of action, the following tabs are accessible:

  • Overview: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-threats/","title":"Threats","text":"

When you click on \"Threats\" in the left-side bar, you access all the \"Threats\" tabs, visible on the top bar on the left. By default, the user directly access the \"Threat Actor (Group)\" tab, but can navigate to the other tabs as well.

From the Threats section, users can access the following tabs:

  • Threat actors (Group): Threat actor (Group) represents a physical group of attackers operating an Intrusion set, using malware and attack infrastructure, etc.
  • Threat actors (Indvidual): Threat actor (Individual) represents a real attacker that can be described by physical and personal attributes and motivations. Threat actor (Individual) operates Intrusion set, uses malware and infrastructure, etc.
  • Intrusion sets: Intrusion set is an important concept in Cyber Threat Intelligence field. It is a consistent set of technical and non-technical elements corresponding of what, how and why a Threat actor acts. it is particularly useful for associating multiple attacks and malicious actions to a defined Threat, even without sufficient information regarding who did them. Often, with you understanding of the threat growing, you will link an Intrusion set to a Threat actor (either a Group or an Individual).
  • Campaigns: Campaign represents a series of attacks taking place in a certain period of time and/or targeting a consistent subset of Organization/Individual.
"},{"location":"usage/exploring-threats/#threat-actors-group-and-individual","title":"Threat actors (Group and Individual)","text":""},{"location":"usage/exploring-threats/#general-presentation","title":"General presentation","text":"

Threat actors are the humans who are building, deploying and operating intrusion sets. A threat actor can be an single individual or a group of attackers (who may be composed of individuals). A group of attackers may be a state-nation, a state-sponsored group, a corporation, a group of hacktivists, etc.

Beware, groups of attackers might be modelled as \"Intrusion sets\" in feeds, as there is sometimes a misunderstanding in the industry between group of people and the technical/operational intrusion set they operate.

When clicking on the Threat actor (Group or Individual) tabs at the top left, you see the list of all the groups of Threat actors or Individual Threat actors you have access to, in respect with your allowed marking definitions. These groups or individual are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware they used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Threat actors.

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

"},{"location":"usage/exploring-threats/#demographic-and-biographic-information","title":"Demographic and Biographic Information","text":"

Individual Threat actors have unique properties to represent demographic and biographic information. Currently tracked demographics include their countries of residence, citizenships, date of birth, gender, and more.

Biographic information includes their eye and hair color, as well as known heights and weights.

An Individual Threat actor can also be tracked as employed by an Organization or a Threat Actor group. This relationship can be set under the knowledge tab.

"},{"location":"usage/exploring-threats/#visualizing-knowledge-associated-with-a-threat-actor","title":"Visualizing Knowledge associated with a Threat actor","text":"

When clicking on a Threat actor Card, you land on its Overview tab. For a Threat actor, the following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Threat actor. Different thematic views are proposed to easily see the victimology, arsenal and techniques used by the Threat actor, etc. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-threats/#intrusion-sets","title":"Intrusion Sets","text":"

An intrusion set is a consistent group of technical elements such as \"tactics, technics and procedures\" (TTP), tools, malware and infrastructure used by a threat actor against one or a number of victims who are usually sharing some characteristics (field of activity, country or region) to reach a similar goal whoever the victim is. The intrusion set may be deployed once or several times and may evolve with time. Several intrusion sets may be linked to one threat actor. All the entities described below may be linked to one intrusion set. There are many debates in the Threat Intelligence community on how to define an intrusion set and how to distinguish several intrusion sets with regards to:

  • their differences
  • their evolutions
  • the possible reuse
  • \"false flag\" type of attacks

As OpenCTI is very customizable, each organization or individual may use these categories as they wish. Instead, it is also possible to use the import feed for the choice of categories.

When clicking on the Intrusion set tab on the top left, you see the list of all the Intrusion sets you have access to, in respect with your allowed marking definitions. These intrusion sets are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware they used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Intrusion set.

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

"},{"location":"usage/exploring-threats/#visualizing-knowledge-associated-with-an-intrusion-set","title":"Visualizing Knowledge associated with an Intrusion set","text":"

When clicking on an Intrusion set Card, you land on its Overview tab. The following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Intrusion Set. Different thematic views are proposed to easily see the victimology, arsenal and techniques used by the Intrusion Set, etc. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/exploring-threats/#campaigns","title":"Campaigns","text":"

A campaign can be defined as \"a series of malicious activities or attacks (sometimes called a \"wave of attacks\") taking place within a limited period of time, against a defined group of victims, associated to a similar intrusion set and characterized by the use of one or several identical malware towards the various victims and common TTPs\". However, a campaign is an investigation element and may not be widely recognized. Thus, a provider might define a series of attacks as a campaign and another as an intrusion set. Campaigns can be attributed to an Intrusion set.

When clicking on the Campaign tab on the top left, you see the list of all the Campaigns you have access to, in respect with your allowed marking definitions. These campaigns are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Campaigns.

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

"},{"location":"usage/exploring-threats/#visualizing-knowledge-associated-with-a-campaign","title":"Visualizing Knowledge associated with a Campaign","text":"

When clicking on an Campaign Card, you land on its Overview tab. The following tabs are accessible:

  • Overview: as described here.
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Campaign. Different thematic views are proposed to easily see the victimology, arsenal and techniques used in the context of the Campaign. As described here.
  • Analyses: as described here.
  • Data: as described here.
  • History: as described here.
"},{"location":"usage/export/","title":"Manual export","text":""},{"location":"usage/export/#introduction","title":"Introduction","text":"

With the OpenCTI platform, you can manually export your intelligence content in the following formats:

  • JSON,
  • CSV,
  • PDF,
  • TXT.

"},{"location":"usage/export/#export-in-structured-or-document-format","title":"Export in structured or document format","text":""},{"location":"usage/export/#generate-an-export","title":"Generate an export","text":"

To export one or more entities you have two possibilities. First you can click on the button \"Open export panel\". The list of pre-existing exports will open, and in the bottom right-hand corner you can configure and generate a new export.

This opens the export settings panel, where you can customize your export according to four fields:

  • desired export format (text/csv, application/pdf, application/vnd.oasis.stix+json, text/plain)
  • export type (simple or full),
  • the max marking definition levels of the elements to be included in the export (a TLP level, for instance). The list of the available max markings is limited by the user allowed markings and its maximum shareable markings (more details about maximum shareable marking definitions in data segregation). For a marking definition type to be taken into account here, a marking definition from this type must be provided. For example, if you select TLP:GREEN for this field, AMBER and RED elements will be excluded but it will not take into account any PAP markings unless one is elected too.
  • the file marking definition levels of the export (a TLP level, for instance). This marking on the file itself will then restrain the access to it in accordance with users' marking definition levels. For example, if a file has the marking TLP:RED and INTERNAL, a user will need to have these marking to see and access the file in the platform.```

The second way is to click directly on the \"Generate an Export\" button to export the content of an entity in the desired format. The same settings panel will open.

Both ways add your export in the Exported files list in the Data tab.

"},{"location":"usage/export/#export-possibilities","title":"Export possibilities","text":"

All entities in your instance can be exported either directly via Generate Export or indirectly via Export List in .json and .csv formats.

"},{"location":"usage/export/#export-a-list-of-entities","title":"Export a list of entities","text":"

You have the option to export either a single element, such as a report, or a collection of elements, such as multiple reports. These exports may contain not only the entity itself but also related elements, depending on the type of export you select: \"simple\" or \"full\". See the Export types (simple and full) section.

You can also choose to export a list of entities within a container. To do so, go to the container's entities tab. For example, for a report, if you only want to retrieve entity type attack pattern and indicators to design a detection strategy, go to the entities tab and select specific elements for export.

"},{"location":"usage/export/#export-types-simple-and-full","title":"Export types (simple and full)","text":"

When you wish to export only the content of a specific entity such as a report, you can choose a \"simple\" export type.

If you also wish to export associated content, you can choose a \"full\" export. With this type of export, the entity will be exported along with all entities directly associated with the central one (first neighbors).

"},{"location":"usage/export/#exports-list-panel","title":"Exports list panel","text":"

Once an export has been created, you can find it in the export list panel. Simply click on a particular export to download it.

You can also generate a new export directly from the Exports list, as explained in the Generate an export section.

"},{"location":"usage/feeds/","title":"Native feeds","text":"

OpenCTI provides versatile mechanisms for sharing data through its built-in feeds, including Live streams, TAXII collections, and CSV feeds.

"},{"location":"usage/feeds/#feed-configuration","title":"Feed configuration","text":"

Feeds are configured in the \"Data > Data sharing\" window. Configuration for all feed types is uniform and relies on the following parameters:

  • Filter setup: The feed can have specific filters to publish only a subset of the platform overall knowledge. Any data that meets the criteria established by the user's feed filters will be shared (e.g. specific types of entities, labels, marking definitions, etc.).
  • Access control: A feed can be either public, i.e. accessible without authentication, or restricted. By default, it's accessible to any user with the \"Access data sharing\" capability, but it's possible to increase restrictions by limiting access to a specific user, group, or organization.

By carefully configuring filters and access controls, you can tailor the behavior of Live streams, TAXII collections, and CSV feeds to align with your specific data-sharing needs.

"},{"location":"usage/feeds/#live-streams","title":"Live streams","text":""},{"location":"usage/feeds/#introduction","title":"Introduction","text":"

Live streams, an exclusive OpenCTI feature, increase the capacity for real-time data sharing by serving STIX 2.1 bundles as TAXII collections with advanced capabilities. What distinguishes them is their dynamic nature, which includes the creation, updating, and deletion of data. Unlike TAXII, Live streams comprehensively resolve relationships and dependencies, ensuring a more nuanced and interconnected exchange of information. This is particularly beneficial in scenarios where sharing involves entities with complex relationships, providing a richer context for the shared data.

In scenarios involving data sharing between two OpenCTI platforms, Live streams emerge as the preferred mechanism. These streams operate like TAXII collections but are notably enhanced, supporting:

  • create, update and delete events depending on the parameters,
  • caching already created entities in the last 5 minutes,
  • resolving relationships and dependencies even out of the filters,
  • can be public (without authentication).

Resolve relationships and dependencies

Dependencies and relationships of entities shared via Live streams, as determined by specified filters, are automatically shared even beyond the confines of these filters. This means that interconnected data, which may not directly meet the filter criteria, is still included in the Live stream. However, OpenCTI data segregation mechanisms are still applied. They allow restricting access to shared data based on factors such as markings or organization. It's imperative to carefully configure and manage these access controls to ensure that no confidential data is shared.

"},{"location":"usage/feeds/#illustrative-scenario","title":"Illustrative scenario","text":"

To better understand how live streams are working, let's take a few examples, from simple to complex.

Given a live stream with filters Entity type: Indicator AND Label: detection. Let's see what happens with an indicator with:

  • Marking definition: TLP:GREEN
  • Author Crowdstrike
  • Relation indicates to the malware Emotet
Action Result in stream (with Avoid dependencies resolution=true) Result in stream (with Avoid dependencies resolution=false) 1. Create an indicator Nothing Nothing 2. Add the label detection Create TLP:GREEN, create CrowdStrike, create the indicator Create TLP:GREEN, create CrowdStrike, create the malware Emotet, create the indicator, create the relationship indicates 3. Remove the label detection Delete the indicator Delete the indicator and the relationship 4. Add the label detection Create the indicator Create the indicator, create the relationship indicates 5. Delete the indicator Delete the indicator Delete the indicator and the relationship

Details on how to consume these Live streams can be found on the dedicated page.

"},{"location":"usage/feeds/#taxii-collections","title":"TAXII Collections","text":"

OpenCTI has an embedded TAXII API endpoint which provides valid STIX 2.1 bundles. If you wish to know more about the TAXII standard, please read the official introduction.

In OpenCTI you can create as many TAXII 2.1 collections as needed.

After creating a new collection, every system with a proper access token can consume the collection using different kinds of authentication (basic, bearer, etc.).

As when using the GraphQL API, TAXII 2.1 collections have a classic pagination system that should be handled by the consumer. Also, it's important to understand that element dependencies (nested IDs) inside the collection are not always contained/resolved in the bundle, so consistency needs to be handled at the client level.

"},{"location":"usage/feeds/#csv-feeds","title":"CSV feeds","text":"

The CSV feed facilitates the automatic generation of a CSV file, accessible via a URL. The CSV file is regenerated and updated at user-defined intervals, providing flexibility. The entries in the file correspond to the information that matches the filters applied and that were created or modified in the platform during the time interval (between the last generation of the CSV and the new one).

CSV size limit

The CSV file generated has a limit of 5 000 entries. If more than 5 000 entities are retrieved by the platform, only the most recent 5 000 will be shared in the file.

"},{"location":"usage/getting-started/","title":"Getting started","text":"

This guide aims to give you a full overview of the OpenCTI features and workflows. The platform can be used in various contexts to handle threats management use cases from a technical to a more strategic level. OpenCTI has been designed as a knowledge graph, taking inputs (threat intelligence feeds, sightings & alerts, vulnerabilities, assets, artifacts, etc.) and generating outputs based on built-in capabilities and / or connectors.

Here are some examples of use cases:

  • Cyber Threat Intelligence knowledge base
  • Detection as code feeds for XDR, EDR, SIEMs, firewalls, proxies, etc.
  • Incident response artifacts & cases management
  • Vulnerabilities management
  • Reporting, alerting and dashboarding on a subset of data

"},{"location":"usage/getting-started/#welcome-dashboard","title":"Welcome dashboard","text":"

The welcome page gives any visitor on the OpenCTI platform an overview of what's happening on the platform. It can be replaced by a custom dashboard, created by a user (or the default dashboard set up in a role, a group or an organization).

"},{"location":"usage/getting-started/#indicators-in-the-dashboard","title":"Indicators in the dashboard","text":""},{"location":"usage/getting-started/#numbers","title":"Numbers","text":"Component Description Intrusion sets Number of intrusion sets . Malware Number of malware. Reports Number of reports. Indicators Number of indicators."},{"location":"usage/getting-started/#charts-lists","title":"Charts & lists","text":"Component Description Most active threats (3 last months) Top active threats (threat actor, intrusion set and campaign) during the last 3 months. Most targeted victims (3 last months) Intensity of the targeting tied to the number of relations targets for a given entities (organization, sector, location, etc.) during the last 3 months. Relationships created Volume of relationships created over the past 12 months. Most active malware (3 last months) Top active malware during the last 3 months. Most active vulnerabilities (3 last months) List of the vulnerabilities with the greatest number of relations over the last 3 months. Targeted countries (3 last months) Intensity of the targeting tied to the number of relations targets for a given country over the past 3 months. Latest reports Last reports ingested in the platform. Most active labels (3 last months) Top labels given to entities during the last 3 months.

Explore the platform

To start exploring the platform and understand how information is structured, we recommend starting with the overview documentation page.

"},{"location":"usage/import-automated/","title":"Automated import","text":"

Automated imports in OpenCTI streamline the process of data ingestion, allowing users to effortlessly bring in valuable intelligence from diverse sources. This page focuses on the automated methods of importing data, which serve as bridges between OpenCTI and diverse external systems, formatting it into a STIX bundle, and importing it into the OpenCTI platform.

"},{"location":"usage/import-automated/#connectors","title":"Connectors","text":"

Connectors in OpenCTI serve as dynamic gateways, facilitating the import of data from a wide array of sources and systems. Every connector is designed to handle specific data types and structures of the source, allowing OpenCTI to efficiently ingest the data.

"},{"location":"usage/import-automated/#connector-behaviors","title":"Connector behaviors","text":"

The behavior of each connector is defined by its development, determining the types of data it imports and its configuration options. This flexibility allows users to customize the import process to their specific needs, ensuring a seamless and personalized data integration experience.

The level of configuration granularity regarding the imported data type varies with each connector. Nevertheless, connectors empower users to specify the date from which they wish to fetch data. This capability is particularly useful during the initial activation of a connector, enabling the retrieval of historical data. Following this, the connector operates in real-time, continuously importing new data from the source.

"},{"location":"usage/import-automated/#connector-ecosystem","title":"Connector Ecosystem","text":"

OpenCTI's connector ecosystem covers a broad spectrum of sources, enhancing the platform's capability to integrate data from various contexts, from threat intelligence providers to specialized databases. The list of available connectors can be found in our connectors catalog. Connectors are categorized into three types: import connectors (the focus here), enrichment connectors, and stream consumers. Further documentation on connectors is available on the dedicated documentation page.

In summary, automated imports through connectors empower OpenCTI users with a scalable, efficient, and customizable mechanism for data ingestion, ensuring that the platform remains enriched with the latest and most relevant intelligence.

"},{"location":"usage/import-automated/#native-automated-import","title":"Native automated import","text":"

In OpenCTI, the \"Data > Ingestion\" section provides users with built-in functions for automated data import. These functions are designed for specific purposes and can be configured to seamlessly ingest data into the platform. Here, we'll explore the configuration process for the four built-in functions: Live Streams, TAXII Feeds, RSS Feeds, and CSV Feeds.

"},{"location":"usage/import-automated/#live-streams","title":"Live streams","text":"

Live Streams enable users to consume data from another OpenCTI platform, fostering collaborative intelligence sharing. Here's a step-by-step guide to configure Live streams synchroniser:

  1. Remote OpenCTI URL: Provide the URL of the remote OpenCTI platform (e.g., https://[domain]; don't include the path).
  2. Remote OpenCTI token: Provide the user token. An administrator from the remote platform must supply this token, and the associated user must have the \"Access data sharing\" privilege.
  3. After filling in the URL and user token, validate the configuration.
  4. Once validated, select a live stream to which you have access.

Additional configuration options:

  • User responsible for data creation: Define the user responsible for creating data received from this stream. Best practice is to dedicate one user per source for organizational clarity. Please see the section \"Best practices\" below for more information.
  • Starting synchronization: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • Take deletions into account: Enable this option to delete data from your platform if it was deleted on the providing stream. (Note: Data won't be deleted if another source has imported it previously.)
  • Verify SSL certificate: Check the validity of the certificate of the domain hosting the remote platform.
  • Avoid dependencies resolution: Import only entities without their relationships. For instance, if the stream shares malware, all the malware's relationships will be retrieved by default. This option enables you to choose not to recover them.
  • Use perfect synchronization: This option is specifically for synchronizing two platforms. If an imported entity already exists on the platform, the one from the stream will overwrite it.

"},{"location":"usage/import-automated/#taxii-feeds","title":"TAXII feeds","text":"

TAXII Feeds in OpenCTI provide a robust mechanism for ingesting TAXII collections from TAXII servers or other OpenCTI instances. Configuring TAXII ingester involves specifying essential details to seamlessly integrate threat intelligence data. Here's a step-by-step guide to configure TAXII ingesters:

  1. TAXII server URL: Provide the root API URL of the TAXII server. For collections from another OpenCTI instance, the URL is in the form https://[domain]/taxii2/root.
  2. TAXII collection: Enter the ID of the TAXII collection to be ingested. For collections from another OpenCTI instance, the ID follows the format 426e3acb-db50-4118-be7e-648fab67c16c.
  3. Authentication type (if necessary): Enter the authentication type. For non-public collections from another OpenCTI instance, the authentication type is \"Bearer token.\" Enter the token of a user with access to the collection (similar to the point 2 of the Live streams configuration above).

TAXII root API URL

Many ISAC TAXII configuration instructions will provide the URL for the collection or discovery service. In these cases, remove the last path segment from the TAXII Server URL in order to use it in OpenCTI. eg. use https://[domain]/tipapi/tip21, and not https://[domain]/tipapi/tip21/collections.

Additional configuration options:

  • User responsible for data creation: Define the user responsible for creating data received from this TAXII feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section \"Best practices\" below for more information.
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.

"},{"location":"usage/import-automated/#rss-feeds","title":"RSS feeds","text":"

RSS Feeds functionality enables users to seamlessly ingest items in report form from specified RSS feeds. Configuring RSS Feeds involves providing essential details and selecting preferences to tailor the import process. Here's a step-by-step guide to configure RSS ingesters:

  1. RSS Feed URL: Provide the URL of the RSS feed from which items will be imported.

Additional configuration options:

  • User responsible for data creation: Define the user responsible for creating data received from this RSS feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section \"Best practices\" below for more information.
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • Default report types: Indicate the report type to be applied to the imported report.
  • Default author: Indicate the default author to be applied to the imported report. Please see the section \"Best practices\" below for more information.
  • Default marking definitions: Indicate the default markings to be applied to the imported reports.

"},{"location":"usage/import-automated/#csv-feeds","title":"CSV feeds","text":"

CSV feed ingester enables users to import CSV files exposed on URLs. Here's a step-by-step guide to configure TAXII ingesters:

  1. CSV URL: Provide the URL of the CSV file exposed from which items will be imported.
  2. CSV Mappers: Choose the CSV mapper to be used to import the data.
  3. Authentication type (if necessary): Enter the authentication type.

CSV mapper

CSV feed functionality is based on CSV mappers. It is necessary to create the appropriate CSV mapper to import the data contained in the file. See the page dedicated to the CSV mapper.

Additional configuration options:

  • User responsible for data creation: Define the user responsible for creating data received from this CSV feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section \"Best practices\" below for more information.
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.

in CSV Mappers, if you created a representative for Marking definition, you could have chosen between 2 options:

  • let the user choose marking definitions
  • Use default marking definitions of the user

This configuration applies when using a CSV Mapper for a CSV Ingester. If you select a CSV Mapper containing the option \"Use default marking definitions of the user\", the default marking definitions of the user you chose to be responsible for the data creation will be applied to all data imported. If you select a CSV Mapper containing the option \"let the user choose marking definitions\", you will be presented with the list of all the marking definitions of the user you chose to be responsible for the data creation (and not yours!)

To finalize the creation, click on \"Verify\" to run a check on the submitted URL with the selected CSV mapper. A valid URL-CSV mapper combination results in the identification of up to 50 entities.

To start your new ingester, click on \"Start\", in the burger menu.

CSV feed ingestion is made possible thanks to the connector \"ImportCSV\". So you can track the progress in \"Data > Ingestion > Connectors\". On a regular basis, the ingestion is updated when new data is added to the CSV feed.

"},{"location":"usage/import-automated/#best-practices-for-feed-import","title":"Best practices for feed import","text":"

Ensuring a secure and well-organized environment is paramount in OpenCTI. Here are two recommended best practices to enhance security, traceability, and overall organizational clarity:

  1. Create a dedicated user for each source: Generate a user specifically for feed import, following the convention [F] Source name for clear identification. Assign the user to the \"Connectors\" group to streamline user management and permission related to data creation. Please see here for more information on this good practice.
  2. Establish a dedicated Organization for the source: Create an organization named after the data source for clear identification. Assign the newly created organization to the \"Default author\" field in feed import configuration if available.

By adhering to these best practices, you ensure independence in managing rights for each import source through dedicated user and organization structures. In addition, you enable clear traceability to the entity's creator, facilitating source evaluation, dashboard creation, data filtering and other administrative tasks.

"},{"location":"usage/import-automated/#digest","title":"Digest","text":"

Users can streamline the data ingestion process using various automated import capabilities. Each method proves beneficial in specific circumstances.

  • Connectors act as bridges to retrieve data from diverse sources and format it for seamless ingestion into OpenCTI.
  • Live Streams enable collaborative intelligence sharing across OpenCTI instances, fostering real-time updates and efficient data synchronization.
  • TAXII Feeds provide a standardized mechanism for ingesting threat intelligence data from TAXII servers or other OpenCTI instances.
  • RSS Feeds facilitate the import of items in report form from specified RSS feeds, offering a straightforward way to stay updated on relevant intelligence.

By leveraging these automated import functionalities, OpenCTI users can build a comprehensive, up-to-date threat intelligence database. The platform's adaptability and user-friendly configuration options ensure that intelligence workflows remain agile, scalable, and tailored to the unique needs of each organization.

"},{"location":"usage/import-files/","title":"Import from files","text":""},{"location":"usage/import-files/#import-mechanisms","title":"Import mechanisms","text":"

The platform provides a seamless process for automatically parsing data from various file formats. This capability is facilitated by two distinct mechanisms.

File import connectors: Currently, there are two connectors designed for importing files and automatically identifying entities.

  • ImportFileStix: Designed to handle STIX-structured files (json or xml format).
  • ImportDocument: Versatile connector supporting an array of file formats, including pdf, text, html, and markdown.

CSV mappers: The CSV mapper is a tailored functionality to facilitate the import of data stored in CSV files. For more in-depth information on using CSV mappers, refer to the CSV Mappers documentation page.

"},{"location":"usage/import-files/#usage","title":"Usage","text":""},{"location":"usage/import-files/#locations","title":"Locations","text":"

Both mechanisms can be employed wherever file uploads are possible. This includes the \"Data\" tabs of all entities and the dedicated panel named \"Data import and analyst workbenches\" located in the top right-hand corner (database logo with a small gear). Importing files from these two locations is not entirely equal; refer to the \"Relationship handling from entity's Data tab\" section below for details on this matter.

"},{"location":"usage/import-files/#entity-identification-process","title":"Entity identification process","text":"

For ImportDocument connector, the identification process involves searching for existing entities in the platform and scanning the document for relevant information. In additions, the connector use regular expressions (regex) to detect IP addresses and domains within the document.

As for the ImportFileStix connector and the CSV mappers, there is no identification mechanism. The imported data will be, respectively, the data defined in the STIX bundle or according to the configuration of the CSV mapper used.

"},{"location":"usage/import-files/#workflow-overview","title":"Workflow overview","text":"
  1. Upload file: Navigate to the desired location, such as the \"Data\" tabs of an entity or the \"Data import and analyst workbenches\" panel. Then, upload the file containing the relevant data by clicking on the small cloud with the arrow inside next to \"Uploaded files\".
  2. Entity identification: For a CSV file, select the connector and CSV mapper to be used by clicking on the icon with an upward arrow in a circle. If it's not a CSV file, the connector will launch automatically. Then, the file import connectors or CSV mappers will identify entities within the uploaded document.
  3. Workbench review and validation: Entities identified by connectors are not immediately integrated into the platform's knowledge base. Instead, they are thoughtfully placed in a workbench, awaiting review and validation by an analyst. Workbenches function as draft spaces, ensuring that no data is officially entered into the platform until the workbench has undergone the necessary validation process. For more information on workbenches, refer to the Analyst workbench documentation page.

Review workbenches

Import connectors may introduce errors in identifying object types or add \"unknown\" entities. Workbenches were established with the intent of reviewing the output of connectors before validation. Therefore, it is crucial to be vigilant when examining the workbench to prevent the import of incorrect data into the platform.

"},{"location":"usage/import-files/#additional-information","title":"Additional information","text":""},{"location":"usage/import-files/#no-workbench-for-csv-mapper","title":"No workbench for CSV mapper","text":"

It's essential to note that CSV mappers operate differently from other import mechanisms. Unlike connectors, CSV mappers do not generate workbenches. Instead, the data identified by CSV mappers is imported directly into the platform without an intermediary workbench stage.

"},{"location":"usage/import-files/#relationship-handling-from-entitys-data-tab","title":"Relationship handling from entity's \"Data\" tab","text":"

When importing a document directly from an entity's \"Data\" tab, there can be an automatic addition of relationships between the objects identified by connectors and the entity in focus. The process differs depending on the type of entity in which the import occurs:

  • If the entity is a container (e.g., Report, Grouping, and Cases), the identified objects in the imported file will be linked to the entity (upon workbench validation). In the context of a container, the object is said to be \"contained\".
  • For entities that are not containers, a distinct behavior unfolds. In this scenario, the identified objects are not linked to the entity, except for Observables. Related to relationships between the Observables and the entity are automatically added to the workbench and created after validation of this one.
"},{"location":"usage/import-files/#file-import-in-content-tab","title":"File import in Content tab","text":"

Expanding the scope of file imports, users can seamlessly add files in the Content tab of Analyses or Cases. In this scenario, the file is directly added as an attachment without utilizing an import mechanism.

"},{"location":"usage/import-files/#user-capability-requirement","title":"User capability requirement","text":"

In order to initiate file imports, users must possess the requisite capability: \"Upload knowledge files.\" This capability ensures that only authorized users can contribute and manage knowledge files within the OpenCTI platform, maintaining a controlled and secure environment for data uploads.

Deprecation warning

Using the ImportDocument connector to parse CSV file is now disallowed as it produces inconsistent results. Please configure and use CSV mappers dedicated to your specific CSV content for a reliable parsing. CSV mappers can be created and configured in the administration interface.

"},{"location":"usage/indicators-lifecycle/","title":"Indicators Lifecycle Management","text":""},{"location":"usage/indicators-lifecycle/#introduction","title":"Introduction","text":"

OpenCTI enforces strict rules to determine the period during which an indicator is effective for usage. This period is defined by the valid_from and valid_until dates. All along its lifecycle, the indicator score will decrease according to configured decay rules. After the indicator expires, the object is marked as revoked and the detection field is automatically set to false. Here, we outline how these dates are calculated within the OpenCTI platform and how the score is updated with decay rules.

"},{"location":"usage/indicators-lifecycle/#setting-validity-dates","title":"Setting validity dates","text":""},{"location":"usage/indicators-lifecycle/#data-source-provided-the-dates","title":"Data source provided the dates","text":"

If a data source provides valid_from and valid_until dates when creating an indicator on the platform, these dates are used without modification. But, if the creation is performed from the UI and the indicator is elligible to be manages by a decay rule, the platform will change this valid_until with the one calculated by the Decay rule.

"},{"location":"usage/indicators-lifecycle/#fallback-rules-for-unspecified-dates","title":"Fallback rules for unspecified dates","text":"

If a data source does not provide validity dates, OpenCTI applies the decay rule matching the indicator to determine these dates. The valid_until date is computed based on the revoke score of the decay rule : it is set at the exact time at which the indicator will reach the revoke score. Past valid_until date, the indicator is marked as revoked.

"},{"location":"usage/indicators-lifecycle/#score-decay","title":"Score decay","text":"

Indicators have an initial score at creation, either provided by data source, or 50 by default. Over time, this score is going to decrease according to the configured decay rules. Score is updated at each reaction point defined for the decay rule matching the indicator at creation.

"},{"location":"usage/indicators-lifecycle/#example","title":"Example","text":"

This URL indicator has matched the Built-in IP and URL decay rule. Its initial score at creation is 100.

Right next to the indicator score, there is a button Lifecycle which enables to open a dialog to see the details of the indicator's lifecyle.

"},{"location":"usage/indicators-lifecycle/#conclusion","title":"Conclusion","text":"

Understanding how OpenCTI calculates validity periods and scores is essential for effective threat intelligence analysis. These rules ensure that your indicators are accurate and up-to-date, providing a reliable foundation for threat intelligence data.

"},{"location":"usage/inferences/","title":"Inferences and reasoning","text":""},{"location":"usage/inferences/#overview","title":"Overview","text":"

OpenCTI\u2019s inferences and reasoning capability is a robust engine that automates the process of relationship creation within your threat intelligence data. This capability, situated at the core of OpenCTI, allows logical rules to be applied to existing relationships, resulting in the automatic generation of new, pertinent connections.

"},{"location":"usage/inferences/#understanding-inferences-and-reasoning","title":"Understanding inferences and reasoning","text":"

Inferences and reasoning serve as OpenCTI\u2019s intelligent engine. It interprets your data logically. By activating specific predefined rules (of which there are around twenty), OpenCTI can deduce new relationships from the existing ones. For instance, if there's a connection indicating an Intrusion Set targets a specific country, and another relationship stating that this country is part of a larger region, OpenCTI can automatically infer that the Intrusion Set also targets the broader region.

"},{"location":"usage/inferences/#key-benefits","title":"Key benefits","text":"
  • Efficiency: Reduces manual workload by automating relationship creation, saving valuable analyst time.
  • Completeness: Fills relationship gaps, ensuring a comprehensive and interconnected threat intelligence database.
  • Accuracy: Minimizes manual input errors by deriving relationships from predefined, accurate logic.
"},{"location":"usage/inferences/#how-it-operates","title":"How it operates","text":"

When you activate an inference rule, OpenCTI continuously analyzes your existing relationships and applies the defined logical rules. These rules are logical statements that define conditions for new relationships. When the set of conditions is met, the OpenCTI creates the corresponding relationship automatically.

For example, if you activate a rule as follows:

IF [Entity A targets Identity B] AND [Identity B is part of Identity C] THEN [Entity A targets Identity C]

OpenCTI will apply this rule to existing data. If it finds an Intrusion Set (\"Entity A\") targeting a specific country (\"Identity B\") and that country is part of a larger region (\"Identity C\"), the platform will automatically establish a relationship between the Intrusion Set and the region.

"},{"location":"usage/inferences/#identifying-inferred-relationships","title":"Identifying inferred relationships","text":"

In the knowledge graphs: Inferred relationships are represented by dotted lines of a different color, distinguishing them from non-inferred relations.

In the lists: In a relationship list, a magic wand icon at the end of the line indicates relationship created by inference.

"},{"location":"usage/inferences/#additional-resources","title":"Additional resources","text":"
  • Administration: To find out about existing inference rules and enable/disable them, refer to the Rules engine page in the Administration section of the documentation.
  • Playbooks: OpenCTI playbooks are highly customizable automation scenarios. This seamless integration allows for further automation, making your threat intelligence processes even more efficient and tailored to your specific needs. More information in our blog post.
"},{"location":"usage/manual-creation/","title":"Manual creations","text":"

Manual data creation in OpenCTI is an intuitive process that occurs throughout the platform. This page provides guidance on two key aspects of manual creation: Entity creation and Relationship creation.

"},{"location":"usage/manual-creation/#entity-creation","title":"Entity creation","text":"

To create an entity:

  1. Navigate to the relevant section: Be on the section of the platform related to the object type you want to create.
  2. Click on the \"+\" icon: Locate the \"+\" icon located at the bottom right of the window.
  3. Fill in entity-specific fields: A form on the right side of the window will appear, allowing to fill in specific fields of the entity. Certain fields are inherently obligatory, and administrators have the option to designate additional mandatory fields (See here for more information).
  4. Click on \"Create\": Once you've filled in the desired fields, click on \"create\" to initiate the entity creation process.

"},{"location":"usage/manual-creation/#relationship-creation","title":"Relationship creation","text":"

Before delving into the creation of relationships between objects in OpenCTI, it's crucial to grasp some foundational concepts. Here are key points to understand:

  • On several aspects, including relationships, two categories of objects must be differentiated: containers (e.g., Reports, Groupings, and Cases) and others. Containers aren't related to but contains objects.
  • Relationships, like all other entities, are objects. They possess fields, can be linked, and share characteristics identical to other entities.
  • Relationships are inherently directional, comprising a \"from\" entity and a \"to\" entity. Understanding this directionality is essential for accurate relationship creation.
  • OpenCTI supports various relationship types, and their usage depends on the entity types being linked. For example, a \"target\" relationship might link malware to an organization, while linking malware to an intrusion set might involve a different relationship type.

Now, let\u2019s explore the process of creating relationships. To do this, we will differentiate the case of containers from the others.

"},{"location":"usage/manual-creation/#for-container","title":"For container","text":"

When it comes to creating relationships within containers in OpenCTI, the process is straightforward. Follow these steps to attach objects to a container:

  1. Navigate to the container: Go to the specific container to which you want to attach an object. This could be a Report, Grouping, or Cases.
  2. Access the \"Entities\" tab: Within the container, locate and access the \"Entities\" tab.
  3. Click on the \"+\" icon: Find the \"+\" icon located at the bottom right of the window.
  4. Search for entities: A side window will appear. Search for the entities you want to add to the container.
  5. Add entities to the container: Click on the desired entities. They will be added directly to the container.
"},{"location":"usage/manual-creation/#for-other","title":"For other","text":"

When creating relationships not involving a container, the creation method is distinct. Follow these steps to create relationships between entities:

  1. Navigate to one of the entities: Go to one of the entities you wish to link. Please be aware that the entity from which you create the relationship will be designated as the \"from\" entity for that relationship. So the decision of which entity to choose for creating the relationship should be considered, as it will impact the outcome.
  2. Access the \"Knowledge\" tab: Within the entity, go to the \"Knowledge\" tab.
  3. Select the relevant categories: In the right banner, navigate to the categories that correspond to the object to be linked. The available categories depend on the type of entity you are currently on. For example, if you are on malware and want to link to a sector, choose \"victimology.\"
  4. Click on the \"+\" icon: Find the \"+\" icon located at the bottom right of the window.
  5. Search for entities: A side window will appear. Search for the entities you want to link.
  6. Add entities and click on \"Continue\": Click on the entities you wish to link. Multiple entities can be selected. Then click on \"Continue\" at the bottom right.
  7. Fill in the relationship form: As relationships are objects, a creation form similar to creating an entity will appear.
  8. Click on \"Create\": Once you've filled in the desired fields, click on \"create\" to initiate the relationship creation process.

"},{"location":"usage/manual-creation/#additional-methods","title":"Additional methods","text":"

While the aforementioned methods are primary for creating entities and relationships, OpenCTI offers versatility, allowing users to create objects in various locations within the platform. Here's a non-exhaustive list of additional places that facilitate on-the-fly creation:

  • Creating entities during relationship creation: During the \"Search for entities\" phase (see above) of the relationship creation process, click on the \"+\" icon to create a new entity directly.
  • Knowledge graph: Within the knowledge graph - found in the knowledge tab of the containers or in the investigation functionality - users can seamlessly create entities or relationships.
  • Inside a workbench: The workbench serves as another interactive space where users can create entities and relationships efficiently.

These supplementary methods offer users flexibility and convenience, allowing them to adapt their workflow to various contexts within the OpenCTI platform. As users explore the platform, they will naturally discover additional means of creating entities and relationships.

Max confidence level

When creating knowledge in the platform, the maximum confidence level of the users is used. Please navigate to this page to understand this concept and the impact it can have on the knowledge creation.

"},{"location":"usage/merging/","title":"Merge objects","text":""},{"location":"usage/merging/#introduction","title":"Introduction","text":"

OpenCTI\u2019s merge capability stands as a pivotal tool for optimizing threat intelligence data, allowing to consolidate multiple entities of the same type. This mechanism serves as a powerful cleanup tool, harmonizing the platform and unifying scattered information. In this section, we explore the significance of this feature, the process of merging entities, and the strategic considerations involved.

"},{"location":"usage/merging/#data-streamlining","title":"Data streamlining","text":"

In the ever-expanding landscape of threat intelligence and the multitude of names chosen by different data sources, data cleanliness is essential. Duplicates and fragmented information hinder efficient analysis. The merge capability is a strategic solution for amalgamating related entities into a cohesive unit. Central to the merging process is the selection of a main entity. This primary entity becomes the anchor, retaining crucial attributes such as name and description. Other entities, while losing specific fields like descriptions, are aliased under the primary entity. This strategic decision preserves vital data while eliminating redundancy.

"},{"location":"usage/merging/#preserving-entity-relationships","title":"Preserving entity relationships","text":"

One of the key feature of the merge capability is its ability to preserve relationships. While merging entities, their interconnected relationships are not lost. Instead, they seamlessly integrate into the new, merged entity. This ensures that the intricate web of relationships within the data remains intact, fostering a comprehensive understanding of the threat landscape.

"},{"location":"usage/merging/#conclusion","title":"Conclusion","text":"

OpenCTI\u2019s merge capability helps improve the quality of threat intelligence data. By consolidating entities and centralizing relationships, OpenCTI empowers analysts to focus on insights and strategies, unburdened by data silos or fragmentation. However, exercising caution and foresight in the merging process is essential, ensuring a robust and streamlined knowledge basis.

"},{"location":"usage/merging/#additional-resources","title":"Additional resources","text":"
  • Administration: To understand how to merge entities and the consideration to take into account, refer to the Merging page in the Administration section of the documentation.
  • Deduplication mechanism: the platform is equipped with deduplication processes that automatically merge data at creation (either manually or by importing data from different sources) if it meets certain conditions.
"},{"location":"usage/nested/","title":"Nested references and objects","text":""},{"location":"usage/nested/#stix-standard","title":"STIX standard","text":""},{"location":"usage/nested/#definition","title":"Definition","text":"

In the STIX 2.1 standard, objects can:

  1. Refer to other objects in directly in their attributes, by referencing one or multiple IDs.
  2. Have other objects directly embedded in the entity.
"},{"location":"usage/nested/#example","title":"Example","text":"
{\n   \"type\": \"intrusion-set\",\n   \"spec_version\": \"2.1\",\n   \"id\": \"intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29\",\n   \"created_by_ref\": \"identity--f431f809-377b-45e0-aa1c-6a4751cae5ff\", // nested reference to an identity\n   \"object_marking_refs\": [\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"], // nested reference to multiple marking defintions\n   \"external_references\": [\n      {\n         \"source_name\": \"veris\",\n         \"external_id\": \"0001AA7F-C601-424A-B2B8-BE6C9F5164E7\",\n         \"url\": \"https://github.com/vz-risk/VCDB/blob/125307638178efddd3ecfe2c267ea434667a4eea/data/json/validated/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json\",    \n      }\n   ],\n   \"created\": \"2016-04-06T20:03:48.000Z\",\n   \"modified\": \"2016-04-06T20:03:48.000Z\",\n   \"name\": \"Bobcat Breakin\",\n   \"description\": \"Incidents usually feature a shared TTP of a bobcat being released within the building containing network access...\",\n   \"aliases\": [\"Zookeeper\"],\n   \"goals\": [\"acquisition-theft\", \"harassment\", \"damage\"]\n}\n

In the previous example, we have 2 nested references to other objects in:

\"created_by_ref\": \"identity--f431f809-377b-45e0-aa1c-6a4751cae5ff\", // nested reference to an identity\n\"object_marking_refs\": [\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"], // nested reference to multiple marking defintions\n

But we also have a nested object within the entity (an External Reference):

\"external_references\": [\n   {\n      \"source_name\": \"veris\",\n      \"external_id\": \"0001AA7F-C601-424A-B2B8-BE6C9F5164E7\",\n      \"url\": \"https://github.com/vz-risk/VCDB/blob/125307638178efddd3ecfe2c267ea434667a4eea/data/json/validated/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json\",    \n   }\n]\n
"},{"location":"usage/nested/#implementation","title":"Implementation","text":""},{"location":"usage/nested/#modelization","title":"Modelization","text":"

In OpenCTI, all nested references and objects are modelized as relationships, to be able to pivot more easily on labels, external references, kill chain phases, marking definitions, etc.

"},{"location":"usage/nested/#import-export","title":"Import & export","text":"

When importing and exporting data to/from OpenCTI, the translation between nested references and objects to full-fledged nodes and edges is automated and therefore transparent for the users. Here is an example with the object in the graph above:

{\n   \"id\": \"file--b6be3f04-e50f-5220-af3a-86c2ca66b719\",\n   \"spec_version\": \"2.1\",\n   \"x_opencti_description\": \"...\",\n   \"x_opencti_score\": 50,\n   \"hashes\": {\n       \"MD5\": \"b502233b34256285140676109dcadde7\"\n   },\n   \"labels\": [\n       \"cookiecutter\",\n       \"clouddata-networks-1\"\n   ],\n   \"external_references\": [\n       {\n           \"source_name\": \"Sekoia.io\",\n           \"url\": \"https://app.sekoia.io/intelligence/objects/indicator--3e6d61b4-d5f0-48e0-b934-fdbe0d87ab0c\"\n       }\n   ],\n   \"x_opencti_id\": \"8a3d108f-908c-4833-8ff4-4d6fc996ce39\",\n   \"type\": \"file\",\n   \"created_by_ref\": \"identity--b5b8f9fc-d8bf-5f85-974e-66a7d6f8d4cb\",\n   \"object_marking_refs\": [\n       \"marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9\"\n   ]\n}\n
"},{"location":"usage/notifications/","title":"Notifications and alerting","text":"

In the evolving landscape of cybersecurity, timely awareness is crucial. OpenCTI empowers users to stay informed and act swiftly through its robust notifications and alerting system. This feature allows users to create personalized triggers that actively monitor the platform for specific events and notify them promptly when these conditions are met.

From individual users tailoring their alert preferences to administrators orchestrating collaborative triggers for Groups or Organizations, OpenCTI's notification system is a versatile tool for keeping cybersecurity stakeholders in the loop.

The main menu \"Notifications and triggers\" for creating and managing notifications is located in the top right-hand corner with the bell icon.

"},{"location":"usage/notifications/#triggers","title":"Triggers","text":"

In OpenCTI, triggers serve as personalized mechanisms for users to stay informed about specific events that align with their cybersecurity priorities. Users can create and manage triggers to tailor their notification experience. Each trigger operates by actively listening to events based on predefined filters and event types, promptly notifying users via chosen notifiers when conditions are met.

"},{"location":"usage/notifications/#trigger-management","title":"Trigger management","text":"

Individual user triggers: Each user possesses the autonomy to craft their own triggers, finely tuned to their unique preferences and responsibilities. By setting up personalized filters and selecting preferred notifiers, users ensure that they receive timely and relevant notifications aligned with their specific focus areas.

Administrative control: Platform administrators have the capability to create and manage triggers for Users, Groups and Organizations. This provides centralized control and the ability to configure triggers that address collective cybersecurity objectives. Users within the designated Group or Organization will benefit from triggers with read-only access rights. These triggers are to be created directly on the User|Group|Organization with whom to share them in \"Settings > Security > Users|Groups|Organizations\".

"},{"location":"usage/notifications/#trigger-filters","title":"Trigger filters","text":"

Leveraging the filters, users can meticulously define the criteria that activate their triggers. This level of granularity ensures that triggers are accurate, responding precisely to events that matter most. Users can tailor filters to consider various parameters such as object types, markings, sources, or other contextual details. They can also allow notifications for the assignment of a Task, a Case, etc.

Beyond filters, a trigger can be configured to respond to three event types: creation, modification, and deletion.

"},{"location":"usage/notifications/#instance-triggers","title":"Instance triggers","text":"

Instance triggers offer a targeted approach to live monitoring by allowing users to set up triggers specific to one or several entities. These triggers, when activated, keep a vigilant eye on a predefined set of events related to the selected entities, ensuring that you stay instantly informed about crucial changes.

"},{"location":"usage/notifications/#creating-instance-triggers","title":"Creating instance triggers","text":"

Method 1: Using the general trigger creation form

  1. Go on the \"Notifications and triggers\" window.
  2. Navigate to the \"Triggers and digests\" tab.
  3. Access the general trigger creation form.
  4. Toggle the switch \"Instance trigger\".
  5. Choose the entities to monitor.

Method 2: Quick subscription

  1. On an entity's overview, locate the \"Instance trigger quick subscription\" button with the bell icon at the top right.
  2. Click on the button to create the instance trigger.
  3. (Optional) Click on it again to modify the instance trigger created.

"},{"location":"usage/notifications/#events-monitored-by-instance-triggers","title":"Events monitored by instance triggers","text":"

An instance trigger set on an entity X actively monitors the following events:

  • Update/Deletion of X: Stay informed when the selected entity undergoes changes or is deleted.
  • Creation/Deletion of relationships: Receive notifications about relationships being added or removed from/to X.
  • Creation/Deletion of related entities: Be alerted when entities that have X in its refs - i.e. contains X, is shared with X, is created by X, etc. - are created or deleted.
  • Adding/Removing X in ref: Stay in the loop when X is included or excluded from the ref of other entities - i.e. adding X in the author of an entity, adding X in a report, etc.).

Entity deletion notification

It's important to note that the notification of entity deletion can occur in two scenarios: - Real entity deletion: When the entity is genuinely deleted from the platform. - Visibility loss: When a modification to the entity results in the user losing visibility for that entity.

"},{"location":"usage/notifications/#digest","title":"Digest","text":"

Digests provide an efficient way to streamline and organize your notifications. By grouping notifications based on selected triggers and specifying the delivery period (daily, weekly, monthly), you gain the flexibility to receive consolidated updates at your preferred time, as opposed to immediate notifications triggered by individual events.

"},{"location":"usage/notifications/#creating-digests","title":"Creating digests","text":"
  1. Go on the \"Notifications and triggers\" window.
  2. Navigate to the \"Triggers and digests\" tab.
  3. Create a new digest.
  4. Configure digest: Set the parameters, including triggers to be included and the frequency of notifications (daily, weekly, monthly).
  5. Choose the notifier(s): Select the notification method(s) (e.g. within the OpenCTI interface, via email, etc.).
"},{"location":"usage/notifications/#benefits-of-digests","title":"Benefits of digests","text":"
  • Organized notifications: Digests enable you to organize and categorize notifications, preventing a flood of individual alerts.
  • Customized delivery: Choose the frequency of digest delivery based on your preferences, whether it's a daily overview, a weekly summary, or a monthly roundup.
  • Reduced distractions: Receive notifications at a scheduled time, minimizing interruptions and allowing you to focus on critical tasks.

Digests enhance your control over notification management, ensuring a more structured and convenient approach to staying informed about important events.

"},{"location":"usage/notifications/#notifiers","title":"Notifiers","text":"

In OpenCTI, notifiers serve as the channels for delivering notifications, allowing users to stay informed about critical events. The platform offers two built-in notifiers, \"Default mailer\" for email notifications and \"User interface\" for in-platform alerts.

"},{"location":"usage/notifications/#notifier-connectors","title":"Notifier connectors","text":"

OpenCTI features built-in notifier connectors that empower users to create personalized notifiers for notification and activity alerting. Three essential connectors are available:

  • Platform mailer connector: Enables sending notifications directly within the OpenCTI platform.
  • Simple mailer connector: Offers a straightforward approach to email notifications with simplified configuration options.
  • Generic webhook connector: Facilitates communication through webhooks.

OpenCTI provides two samples of webhook notifiers designed for Teams integration.

"},{"location":"usage/notifications/#configuration-and-access","title":"Configuration and Access","text":"

Notifiers are manageable in the \"Settings > Customization > Notifiers\" window and can be restricted through Role-Based Access Control (RBAC). Administrators can restrict access to specific Users, Groups, or Organizations, ensuring controlled usage.

For guidance on configuring custom notifiers and explore detailed setup instructions, refer to the dedicated documentation page.

"},{"location":"usage/overview/","title":"Overview","text":""},{"location":"usage/overview/#introduction","title":"Introduction","text":"

The following chapter aims at giving the reader a step-by-step description of what is available on the platform and the meaning of the different tabs and entries.

When the user connects to the platform, the home page is the Dashboard. This Dashboard contains several visuals summarizing the types and quantity of data recently imported into the platform.

Dashboard

To get more information about the components of the default dashboard, you can consult the Getting started.

The left side panel allows the user to navigate through different windows and access different views and categories of knowledge.

"},{"location":"usage/overview/#structure","title":"Structure","text":""},{"location":"usage/overview/#the-hot-knowledge","title":"The \"hot knowledge\"","text":"

The first part of the platform in the left menu is dedicated to what we call the \"hot knowledge\", which means this is the entities and relationships which are added on a daily basis in the platform and which generally require work / analysis from the users.

  • Analyses: all containers which convey relevant knowledge such as reports, groupings and malware analyses.
  • Cases: all types of case like incident responses, requests for information, for takedown, etc.
  • Events: all incidents & alerts coming from operational systems as well as sightings.
  • Observations: all technical data in the platform such as observables, artifacts and indicators.
"},{"location":"usage/overview/#the-cold-knowledge","title":"The \"cold knowledge\"","text":"

The second part of the platform in the left menu is dedicated to the \"cold knowledge\", which means this is the entities and relationships used in the hot knowledge. You can see this as the \"encyclopedia\" of all pieces of knowledge you need to get context: threats, countries, sectors, etc.

  • Threats: all threats entities from campaigns to threat actors, including intrusion sets.
  • Arsenal: all tools and pieces of malware used and/or targeted by threats, including vulnerabilities.
  • Techniques: all objects related to tactics and techniques used by threats (TTPs, etc.).
  • Entities: all non-geographical contextual information such as sectors, events, organizations, etc.
  • Locations: all geographical contextual information, from cities to regions, including precise positions.
"},{"location":"usage/overview/#hide-categories","title":"Hide categories","text":"

You can customize the experience in the platform by hiding some categories in the left menu, whether globally or for a specific role.

"},{"location":"usage/overview/#hide-categories-globally","title":"Hide categories globally","text":"

In the Settings > Parameters, it is possible for the platform administrator to hide categories in the platform for all users.

"},{"location":"usage/overview/#hide-categories-in-roles","title":"Hide categories in roles","text":"

In OpenCTI, the different roles are highly customizable. It is possible to defined default dashboards, triggers, etc. but also be able to hide categories in the roles:

"},{"location":"usage/overview/#presentation-of-a-typical-page-in-opencti","title":"Presentation of a typical page in OpenCTI","text":"

While OpenCTI features numerous entities and tabs, many of them share similarities, with only minor differences arising from specific characteristics. These differences may involve the inclusion or exclusion of certain fields, depending on the nature of the entity.

In this part will only be detailed a general outline of a \"typical\" OpenCTI page. The specifies of the different entities will be detailed in the corresponding pages below (Activities and Knowledge).

"},{"location":"usage/overview/#overview_1","title":"Overview","text":"

In the Overview tab on the entity, you will find all properties of the entity as well as the recent activities.

First, you will find the Details section, where are displayed all properties specific to the type of entity you are looking at, an example below with a piece of malware:

Thus, in the Basic information section, are displayed all common properties to all objects in OpenCTI, such as the marking definition, the author, the labels (i.e. tags), etc.

Below these two sections, you will find latest modifications in the Knowledge base related to the Entity:

  • Latest created relationships: display the latest relationships that have been created from or to this Entity. For example, latest Indicators of Compromise and associated Threat Actor of a Malware.
  • latest containers about the object: display all the Cases and Analyses that contains this Entity. For example, the latest Reports about a Malware.
  • External references: display all the external sources associated with the Entity. You will often find here links to external reports or webpages from where Entity's information came from.
  • History: display the latest chronological modifications of the Entity and its relationships that occurred in the platform, in order to traceback any alteration.

Last, all Notes written by users of the platform about this Entity are displayed in order to access unstructured analysis comments.

"},{"location":"usage/overview/#knowledge","title":"Knowledge","text":"

In the Knowledge tab, which is the central part of the entity, you will find all the Knowledge related to the current entity. The Knowledge tab is different for Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) entities than for all the other entity types.

  • The Knowledge tab of those entities (who represents Analyses or Cases that can contains a collection of Objects) is the place to integrate and link together entities. For more information on how to integrate information in OpenCTI using the knowledge tab of a report, please refer to the part Manual creation.
  • The Knowledge tabs of any other entity (that does not aim to contain a collection of Objects) gather all the entities which have been at some point linked to the entity the user is looking at. For instance, as shown in the following capture, the Knowledge tab of Intrusion set APT29, gives access to the list of all entities APT29 is attributed to, all victims the intrusion set has targeted, all its campaigns, TTPs, malware etc. For entities to appear in these tabs under Knowledge, they need to have been linked to the entity directly or have been computed with the inference engine.

"},{"location":"usage/overview/#focus-on-indicators-and-observables","title":"Focus on Indicators and Observables","text":"

The Indicators and Observables section offers 3 display modes: - The entities view, which displays the indicators/observables linked to the entity. - The relationship view, which displays the various relationships between the indicators/observables linked to the entity and the entity itself. - The contextual view, which displays the indicators/observables contained in the cases and analyses that contain the entity.

"},{"location":"usage/overview/#content","title":"Content","text":"

The Content tab allows for uploading and creating outcomes documents related to the content of the current entity (in PDF, text, HTML or markdown files). This specific tab enable to previzualize, manage and write deliverable associated with the entity. For example an analytic report to share with other teams, a markdown files to feed a collaborative wiki with, etc.

The Content tab is available for a subset of entities: Report, Incident, Incident response, Request for Information, and Request for Takedown.

"},{"location":"usage/overview/#analyses","title":"Analyses","text":"

The Analyses tab contains the list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) in which the entity has been identified.

By default, this tab display the list, but you can also display the content of all the listed Analyses on a graph, allowing you to explore all their Knowledge and have a glance of the context around the Entity.

"},{"location":"usage/overview/#data","title":"Data","text":"

The Data tab contains documents that are associated to the object and were either:

  • Uploaded to the platform : for instance the PDF document containing the text of the report
  • Generated from the platform to be downloaded : a JSON or CSV file containing information on the object and generated by the user.
  • associated to an external reference

Analyst Workbench can also be created from here. They will contain the entity by default.

In addition, the Data tab of Threat actors (group), Threat actors (individual), Intrusions sets, Organizations, Individuals have an extra panel:

  • Pictures Management: allows to manage the profile pictures attached to the entity.

"},{"location":"usage/overview/#history","title":"History","text":"

The History tab displays the history of change of the Entity, update of attributes, creation of relations, ...

Because of the volumes of information the history is written in a specific index that consume the redis stream to rebuild the history for the UI.

"},{"location":"usage/overview/#less-frequent-tabs","title":"Less frequent tabs","text":"
  • The Observables tab (for Reports and Observed data): A table containing all SCO (Stix Cyber Observable) contained in the Report or the Observed data, with search and filters available. It also displays if the SCO has been added directly or through inferences with the reasoning engine
  • the Entities tab (for Reports and Observed data): A table containing all SDO (Stix Domain Objects) contained in the Report or the Observed data, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • Observables:
  • the Sightings tab (for Indicators and Observables): A table containing all Sightings relationships corresponding to events in which Indicators (IP, domain name, URL, etc.) are detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or EDR.
"},{"location":"usage/pivoting/","title":"Pivot and investigate","text":"

In OpenCTI, all data are structured as an extensive knowledge graph, where every element is interconnected. The investigation functionality provides a powerful tool for pivoting on any entity or relationship within the platform. Pivoting enables users to explore and analyze connections between entities and relationships, facilitating a comprehensive understanding of the data.

To access investigations, navigate to the top right corner of the toolbar:

Access restriction

When an investigation is created, it is initially visible only to the creator, allowing them to work on the investigation before deciding to share it. The sharing mechanism is akin to that of dashboards. For further details, refer to the Access control section in the dashboard documentation page.

"},{"location":"usage/pivoting/#perform-investigation","title":"Perform investigation","text":""},{"location":"usage/pivoting/#manipulate-entity","title":"Manipulate entity","text":"

We can add any existing entity of the platform to your investigation.

After adding an entity, we can choose the entity and view its details in the panel that appears on the right of the screen.

On each node, we'll notice a bullet with a number inside, serving as a visual indication of how many entities are linked to it but not currently displayed in the graph. Keep in mind that this number is an approximation, which is why there's a \"+\" next to it. If there's no bullet displayed, it means there's nothing to expand from this node.

"},{"location":"usage/pivoting/#expansion","title":"Expansion","text":"

To incorporate these linked entities into the graph, we just have to expand the nodes. Utilize the button with a 4-arrows logo in the mentioned menu, or double-click on the entity directly. This action opens a new window where we can choose the types of entities and relationships we wish to expand.

For instance, in the image above, selecting the target Malware and the relationship Uses implies expanding in my investigation graph all Malware linked to this node with a relationship of type Uses.

"},{"location":"usage/pivoting/#roll-back-expansion","title":"Roll back expansion","text":"

Expanding a graph can add a lot of entities and relations, making it not only difficult to read but sometimes counterproductive since it brings entities and relations that are not useful to your investigations. To solve this problem, there is a button to undo the last expansion.

When clicking on this button, we will retrieve the state in which your graph was before your expansion. As a result, please note that all add or remove actions made since the last expansion will be lost: in other words, if you have expanded your graph, and then have added some entities in your graph, when clicking on rollback button, the entities that you have added will not be in your graph.

You can roll back your investigation graph up to the last 10 expand actions.

"},{"location":"usage/pivoting/#manipulate-relationship","title":"Manipulate relationship","text":"

We can create a relationship between entities directly within our investigation. To achieve this, select multiple entities by clicking on them while holding down the shift key. Subsequently, a button appears at the bottom right to create one (or more, depending on the number of entities selected) relationships.

Relationship creation

Creating a relationship in the investigation graph will generate the relationship in your knowledge base.

"},{"location":"usage/pivoting/#capitalize-on-an-investigation","title":"Capitalize on an investigation","text":""},{"location":"usage/pivoting/#export-investigation","title":"Export investigation","text":"

Users have the capability to export investigations, providing a way to share, document, or archive their findings.

  • PDF and image formats: Users can export investigations in either PDF or image format, offering flexibility in sharing and documentation.
  • STIX bundle: The platform allows the export of the entire content of an investigation graph as a STIX bundle. In the STIX format, all objects within the investigation graph are automatically aggregated into a Report object.

"},{"location":"usage/pivoting/#turn-investigation-into-a-container","title":"Turn investigation into a container","text":"

Users can efficiently collect and consolidate the findings of an investigation by adding the content into dedicated containers. The contents of an investigation can be imported into various types of containers, including:

  • Grouping
  • Incident Response
  • Report
  • Request for Information
  • Request for Takedown

We have the flexibility to choose between creating a new container on the fly or adding investigation content to an existing container.

After clicking on the ADD button, the browser will redirect to the Knowledge tab of the container where we added the content of our investigation. If we added it to multiple containers, the redirection will be to the first of the list.

"},{"location":"usage/reliability-confidence/","title":"Reliability and Confidence","text":""},{"location":"usage/reliability-confidence/#generalities","title":"Generalities","text":"

In (Cyber) Threat Intelligence, evaluation of information sources and of information quality is one of the most important aspect of the work. It is of the utter most importance to assess situations by taking into account reliability of the sources and credibility of the information.

This concept is foundational in OpenCTI, and have real impact on:

  • the data deduplication process
  • the data stream filtering for ingestion and sharing
"},{"location":"usage/reliability-confidence/#what-is-the-reliability-of-a-source","title":"What is the Reliability of a source?","text":"

Reliability of a source of information is a measurement of the trust that the analyst can have about the source, based on the technical capabilities or history of the source. Is the source a reliable partner with long sharing history? A competitor? Unknown?

Reliability of sources are often stated at organizational level, as it requires an overview of the whole history with it.

In the Intelligence field, Reliability is often notated with the NATO Admiralty code.

"},{"location":"usage/reliability-confidence/#what-is-confidence-of-an-information","title":"What is Confidence of an information?","text":"

Reliability of a source is important but even a trusted source can be wrong. Information in itself has a credibility, based on what is known about the subject and the level of corroboration by other sources.

Credibility is often stated at the analyst team level, expert of the subject, able to judge the information with its context.

In the Intelligence field, Confidence is often notated with the NATO Admiralty code.

Why Confidence instead of Credibility?

Using both Reliability and Credibility is an advanced use case for most of CTI teams. It requires a mature organization and a well staffed team. For most of internal CTI team, a simple confidence level is enough to forge assessment, in particular for teams that concentrate on technical CTI.

Thus in OpenCTI, we have made the choice to fuse the notion of Credibility with the Confidence level that is commonly used by the majority of users. They have now the liberty to push forward their practice and use both Confidence and Reliability in their daily assessments.

"},{"location":"usage/reliability-confidence/#reliability-open-vocabulary","title":"Reliability open vocabulary","text":"

Reliability value can be set for every Entity in the platform that can be Author of Knowledge:

  • Organizations
  • Individuals
  • Systems
  • and also Reports

Reliability on Reports allows you to specify the reliability associated to the original author of the report if you received it through a provider.

For all Knowledge in the platform, the reliability of the source of the Knowledge (author) is displayed in the Overview. This way, you can always forge your assessment of the provided Knowledge regarding the reliability of the author.

You can also now filter entities by the reliability of its author.

Tip

This way, you may choose to feed your work with only Knowledge provided by reliable sources.

Reliability is an open vocabulary that can be customized in Settings -> Taxonomies -> Vocabularies : reliability_ov.

Info

The setting by default is the Reliability scale from NATO Admiralty code. But you can define whatever best fit your organization.

"},{"location":"usage/reliability-confidence/#confidence-scale","title":"Confidence scale","text":"

Confidence level can be set for:

  • Analyses: Report, Grouping, Malware analysis, Notes
  • Cases: Incident Response, Request for Information, Request for Takedown, Feedback
  • Events: Incident, Sighting, Observed data
  • Observations: Indicator, Infrastructure
  • Threats: Threat actor (Group), Threat actor (Individual), Intrusion Set, Campaign
  • Arsenal: Malware, Channel, Tool, Vulnerability

For all of these entities, the Confidence level is displayed in the Overview, along with the Reliability. This way, you can rapidly assess the Knowledge with the Confidence level representing the credibility/quality of the information.

"},{"location":"usage/reliability-confidence/#confidence-scale-customization","title":"Confidence scale customization","text":"

Confidence level is a numerical value between 0 and 100. But Multiple \"Ticks\" can be defined and labelled to provide a meaningful scale.

Confidence level can be customized for each entity type in Settings > Customization > Entity type.

As such customization can be cumbersome, three confidence level templates are provided in OpenCTI:

  • Admiralty: corresponding to the Admiralty code's credibility scale
  • Objective: corresponding to a full objective scale, aiming to leave any subjectivity behind. With this scale, an information confidence is:
    • \"Cannot be judged\": there is no data regarding the credibility of the information
    • \"Told\": the information is known because it has been told to the source. The source doesn't verify it by any means.
    • \"Induced\": the information is the result of an analysis work and is based on other similar information assumed to be true.
    • \"Deduced\": the information is the result of an analysis work, and is a logical conclusion of other information assumed to be true.
    • \"Witnessed\": the source have observed itself the described situation or object.
  • standard: the historic confidence level scale in OpenCTI defining a Low, Med and High level of confidence.

It is always possible to modify an existing template to define a custom scale adapted to your context.

Tip

If you use the Admiralty code setting for both reliability and Confidence, you will find yourself with the equivalent of NATO confidence notation in the Overview of your different entities (A1, B2, C3, etc.)

"},{"location":"usage/reliability-confidence/#max-confidence-level","title":"Max confidence level","text":""},{"location":"usage/reliability-confidence/#overview","title":"Overview","text":"

We know that in organizations, different users do not always have the same expertise or seniority. As a result, some specific users can be more \"trusted\" when creating or updating knowledge than others. Additionally, because connectors, TAXII feeds and streams are all linked to respectively one user, it is important to be able to differentiate which connector, stream or TAXII feed is more trustable than others.

This is why we have introduced the concept of max confidence level to tackle this use case.

Max confidence level per user allows organizations to fine tune their users to ensure knowledge updated and created stays as consistent as possible.

The maximum confidence level can be set at the Group level or at the User level, and can be overridden by entity type for fine-tuning your confidence policy.

"},{"location":"usage/reliability-confidence/#overall-way-of-working","title":"Overall way of working","text":"

The overall idea is that users with a max confidence level lower than a confidence level of an entity cannot update or delete this entity.

Also, in a conservative approach, when 2 confidence levels are possible, we would always take the lowest one.

To have a detailed understanding of the concept, please browse through this diagram:

"},{"location":"usage/reliability-confidence/#user-effective-confidence-level","title":"User effective confidence level","text":"

User and group confidence level configuration shall be viewed as:

  • a maximum confidence level between 0 and 100 (optional for users, mandatory for groups);
  • a list of overrides (a max confidence level between 0 and 100) per entity type (optional).

The user's effective confidence level is the result of this configuration from multiple sources (user and their groups).

To compute this value, OpenCTI uses the following strategy:

  • effective maximum confidence is the maximum value found in the user's groups;
  • effective overrides per entity type are cumulated from all groups, taking the maximum value if several overrides are set on the same entity type
  • if a user maximum confidence level is set, it overrides everything from groups, including the overrides per entity type defined at group level
  • if not, but the user has specific overrides per entity types, they override the corresponding confidence levels per entity types coming from groups
  • if a user has the administrator's \"Bypass\" capability, the effective confidence level will always be 100 without overrides, regardless of the group and user configuration on confidence level

The following diagram describes the different use-cases you can address with this system.

"},{"location":"usage/reliability-confidence/#how-to-set-a-confidence-level","title":"How to set a confidence level","text":"

You can set up a maximum confidence levels from the Confidences tab in the edition panel of your user or group. The value can be selected between 0 and 100, or using the admiralty scale selector.

At the group level, the maximum confidence level is mandatory, but is optional at the user level (you have to enable it using the corresponding toggle button).

"},{"location":"usage/reliability-confidence/#how-to-override-a-max-confidence-level-per-entity-type","title":"How to override a max confidence level per entity type","text":"

You also have the possibility to override a max confidence level per entity type, limited to Stix Domain Objects.

You can visualize the user's effective confidence level in the user's details view, by hovering the corresponding tooltip. It describes where the different values might come from.

"},{"location":"usage/reliability-confidence/#usage-in-opencti","title":"Usage in OpenCTI","text":""},{"location":"usage/reliability-confidence/#example-with-the-admiralty-code-template","title":"Example with the admiralty code template","text":"

Your organization have received a report from a CTI provider. At your organization level, this provider is considered as reliable most of the time and its reliability level has been set to \"B - Usually Reliable\" (your organization uses the Admiralty code).

This report concerns ransomware threat landscape and have been analysed by your CTI analyst specialized in cybercrime. This analyst has granted a confidence level of \"2 - Probably True\" to the information.

As a technical analyst, through the cumulated reliability and Confidence notations, you now know that the technical elements of this report are probably worth consideration.

"},{"location":"usage/reliability-confidence/#example-with-the-objective-template","title":"Example with the Objective template","text":"

As a CTI analyst in a governmental CSIRT, you build up Knowledge that will be shared within the platform to beneficiaries. Your CSIRT is considered as a reliable source by your beneficiaries, even if you play a role of a proxy with other sources, but your beneficiaries need some insights about how the Knowledge has been built/gathered.

For that, you use the \"Objective\" confidence scale in your platform to provide beneficiaries with that. When the Knowledge is the work of the investigation of your CSIRT, either from incident response or attack infrastructure investigation, you set the confidence level to \"Witnessed\", \"Deduced\" or \"Induced\" (depending on if you observed directly the data, or inferred it during your research). When the information has not been verified by the CSIRT but has value to be shared with beneficiaries, you can use the \"Told\" level to make it clear to them that the information is probably valuable but has not been verified.

"},{"location":"usage/search/","title":"Search for knowledge","text":"

In OpenCTI, you have access to different capabilities to be able to search for knowledge in the platform. In most cases, a search by keyword can be refined with additional filters for instance on the type of object, the author etc.

"},{"location":"usage/search/#global-search","title":"Global search","text":"

The global search is always available in the top bar of the platform.

This search covers all STIX Domain Objects (SDOs) and STIX Cyber Observables (SCOs) in the platform. The search results are sorted according to the following behaviour:

  • Priority 1 for exact matching of the keyword in one attribute of the objects.
  • Priority 2 for partial matching of the keyword in the name, the aliases and the description attributes (full text search).
  • Priority 3 for partial matching of the keyword in all other attributes (full text search).

If you get unexpected result, it is always possible to add some filters after the initial search:

Also, using the Advanced search button, it is possible to directly put filters in a global search:

Advanced filters

You have access to advanced filters all accross the UI, if you want to know more about how to use these filters with the API or the Python library, don't hesitate to read the dedicated page

"},{"location":"usage/search/#full-text-search-in-files-content","title":"Full text search in files content","text":"

Enterprise edition

Full text search in files content is available under the \"OpenCTI Enterprise Edition\" license.

Please read the dedicated page to have all information

It's possible to extend the global search by keywords to the content of documents uploaded to the platform via the Data import tab, or directly linked to an entity via its Data tab.

It is particularly useful to enable Full text indexing to avoid missing important information that may not have been structured within the platform. This situation can arise due to a partial automatic import of document content, limitations of a connector, and, of course, errors during manual processing.

In order to search in files, you need to configure file indexing.

"},{"location":"usage/search/#bulk-search","title":"Bulk search","text":"

The bulk search capabilities is available in the top bar of the platform and allows you to copy paste a list of keyword or objects (ie. list of domains, list of IP addresses, list of vulnerabilities, etc.) to search in the platform:

When searching in bulk, OpenCTI is only looking for an exact match in some properties:

  • name
  • aliases
  • x_opencti_aliases
  • x_mitre_id
  • value
  • subject
  • abstract
  • hashes.MD5
  • hashes.SHA-1
  • hashes.SHA-256
  • hashes.SHA-512
  • x_opencti_additional_names

When something is not found, it appears in the list as Unknown and will be excluded if you choose to export your search result in a JSON STIX bundle or in a CSV file.

"},{"location":"usage/search/#contextual-search","title":"Contextual search","text":"

In most of the screens of knowledge, you always have a contextual search bar allowing you to filter the list you are on:

The search keyword used here is taken into account if you decide to export the current view in a file such as a JSON STIX bundle or a CSV file.

"},{"location":"usage/search/#other-search-bars","title":"Other search bars","text":"

Some other screens can contain search bars for specific purposes. For instance, in the graph views to filter the nodes displayed on the graph:

"},{"location":"usage/tips-widget-creation/","title":"Pro-tips on widget creation","text":"

Previously, the creation of widgets has been covered. To help users being more confident in creating widgets, here are some details to master the widget creation.

"},{"location":"usage/tips-widget-creation/#how-to-choose-the-appropriate-widget-visualization-for-your-use-case","title":"How to choose the appropriate widget visualization for your use case?","text":"

We can classify the widgets in 3 different types.

"},{"location":"usage/tips-widget-creation/#single-dimension-widgets","title":"Single dimension widgets","text":"

Use these widgets when you would like to display information about one single type of object (entity or relation).

  • Widget visualizations: number, list, list (distribution), timeline, donuts, radar, map, bookmark, tree map.
  • Use case example: view the amount of malware in platform (number widget), view the top 10 threat actor group target a specific country (distribution list widget), etc.
"},{"location":"usage/tips-widget-creation/#multi-dimension-widgets","title":"Multi dimension widgets","text":"

Use these widgets if you would like to compare or have some insights about similar types of object (up to 5).

  • Widget visualizations: line, area, heatmap, vertical bars.
  • Use case example: view the amount of malware, intrusion sets, threat actor groups added in the course of last month in the platform (line or area widget).

Type of object in widget

These widgets need to use the same \"type\" of object to work properly. You always need to add relationships in the filter view if you have selected a \"knowledge graph\" perspective. If you have selected the knowledge graph entity, adding \"Entities\" (click on + entities) will not work, since you are not counting the same things.

"},{"location":"usage/tips-widget-creation/#break-down-widgets","title":"Break down widgets","text":"

Use this widget if you want to divide your data set into smaller parts to make it clearer and more useful for analysis.

  • Widget visualization: horizontal bars.
  • Use case example: view the list of malware targeting a country breakdown by the type of malware.

"},{"location":"usage/tips-widget-creation/#adding-datasets-to-your-widget","title":"Adding datasets to your widget","text":"

Adding datasets can serve two purposes: comparing data or breakdown a view to have deeper understanding on what a specific dataset is composed of.

"},{"location":"usage/tips-widget-creation/#use-case-1-compare-several-datasets","title":"Use Case 1: compare several datasets","text":"

As mentioned in How to choose the appropriate widget visualization for your use case? section you can add data sets to compare different data. Make sure to add the same type of objects (entities or relations) to be able to compare the same objects, by using access buttons like +, + Relationships, or + Entities.

You can add up to 5 different data sets. The Label field allows you to name a data set, and this label can then be shown as a legend in the widget using the Display legend button in the widget parameters (see the next section).

"},{"location":"usage/tips-widget-creation/#use-case-2-break-down-your-chart","title":"Use case 2: break down your chart","text":"

As mentioned in How to choose the appropriate widget visualization for your use case? section you can add data sets to decompose your graph into smaller meaningful chunks. In the below points, you can find some use cases that will help you understand how to structure your data.

You can break down a view either by entity or by relations, depending on what you need to count.

"},{"location":"usage/tips-widget-creation/#break-down-by-entity","title":"Break down by entity","text":"

Use case example: I need to understand what are the most targeted countries by malware, and have a breakdown for each country by malware type.

Process:

  1. To achieve this use case, you first need to select the horizontal bar vizualisation.
  2. Then you need to select the knowledge graph perspective.

In the filters view:

  1. Then input your main query Source type = Malware AND Target type = Countries AND Relation type = Targets. Add a label to your dataset.
  2. Add an entity data set by using access button + Entities.
  3. Add the following filters Entity type = Malware AND In regards of = targets. Add a label to your dataset.

In the parameter view:

  1. Attribute (of your relation) = entity (so that you display the different entities values)
  2. Display the source toggle = off
  3. Attribute (of your entity malware) = Malware type (since you want to break down your relations by the malware types)

As a result, you get a list of countries broken down by malware types.

"},{"location":"usage/tips-widget-creation/#break-down-by-relation","title":"Break down by relation","text":"

Use case example: I need to understand what are the top targeting malware and have a breakdown of the top targets per malware

Process:

  1. To achieve this use case, you first need to select the horizontal bar vizualisation.
  2. Then you need to select the knowledge graph perspective.

In the filters view:

  1. Then input your main query Source type = Malware AND Relation type = Targets. Add a label to your dataset.
  2. Add a relation data set by using access button + Relationships
  3. Add the following filters Source type = Malware AND Relation type = targets. Add a label to your dataset.

In the parameter view:

  1. Attribute (of your relation): entity (so that you display the different entities values)
  2. Display the source toggle = on
  3. Attribute (of your entity malware) = Malware type (since you want to break down your relations by the malware types)
  4. Display the source toggle = off

As a result, you get a list of malware with the breakdown of their top targets.

"},{"location":"usage/tips-widget-creation/#more-use-cases","title":"More use cases","text":"

To see more use cases, feel free to have a look at this blog post that will provide you additional information.

"},{"location":"usage/widgets/","title":"Widget creation","text":"

Creating widgets on the dashboard involves a four-step configuration process. By navigating through these configuration steps, users can design widgets that meet their specific requirements.

"},{"location":"usage/widgets/#widget-configuration","title":"Widget configuration","text":""},{"location":"usage/widgets/#1-visualization","title":"1. Visualization","text":"

Users can select from 15 diverse visualization options to highlight different aspects of their data. This includes simple views like counters and lists, as well as more intricate views like heatmaps and trees. The chosen visualization impacts the available perspectives and parameters, making it crucial to align the view with the desired data observations. Here are a few insights:

  • Line and Area views: Ideal for visualizing activity volumes over time.
  • Horizontal bar views: Designed to identify top entities that best satisfy applied filters (e.g., top malware targeting the Finance sector).
  • Tree views: Useful for comparing activity volumes.
  • ...

"},{"location":"usage/widgets/#2-perspective","title":"2. Perspective","text":"

A perspective is the way the platform will count the data to display in your widgets:

  • Entities Perspective: Focuses on entities, allowing observation of simple knowledge based on defined filters and criteria. The count will be based on entities only.
  • Knowledge Graph Perspective: Concentrates on relationships, displaying intricate knowledge derived from relationships between entities and specified filters. The count will be based on relations only.
  • Activity & History Perspective: Centers on activities within the platform, not the knowledge content. This perspective is valuable for monitoring user and connector activities, evaluating data sources, and more.

"},{"location":"usage/widgets/#3-filters","title":"3. Filters","text":""},{"location":"usage/widgets/#generic-knowledge","title":"Generic knowledge","text":"

Filters vary based on the selected perspective, defining the dataset to be utilized in the widget. Filters are instrumental in narrowing down the scope of data for a more focused analysis.

While filters in the \"Entities\" and \"Activity & History\" perspectives align with the platform's familiar search and feed creation filters, the \"Knowledge Graph\" perspective introduces a more intricate filter configuration.Therefore, they need to be addressed in more detail.

"},{"location":"usage/widgets/#filter-in-the-context-of-knowledge-graph","title":"Filter in the context of Knowledge Graph","text":"

Two types of filters are available in the Knowledge Graph perspective:

  • Main query filter

    • Classic filters (gray): Define the relationships to be retrieved, forming the basis on which the widget displays data. Remember, statistics in the Knowledge Graph perspective are based on relationships.
  • Pre-query filters

    • Pre-query filters are used to provide to your main query a specific dataset. In other words, instead of making a query on the whole data set of your platform, you can already target a subset of data that will match certain criteria. They are two types of pre-query filters:
      • Dynamic filters on the source (orange): Refine data by filtering on entities positioned as the source (in the \"from\" position) of the relationship.
      • Dynamic filters on the target (green): Refine data by filtering on entities positioned as the target (in the \"to\" position) of the relationship.

Pre-query limitation

The pre-query is limited to 5000 results. If your pre-query results in having more than 5000 results, your widget will only display statistics based on these 5000 results matching your pre-query, resulting in a wrong view. To avoid this issue, be specific in your pre-query filters.

Example scenario:

Let's consider an example scenario: Analyzing the initial access attack patterns used by intrusion sets targeting the finance sector.

  1. Classic filters: Define the relationships associated with the use of attack patterns by intrusion sets
  2. Dynamic filters on the source (Orange): Narrow down the data by filtering on intrusion sets targeting the finance sector.
  3. Dynamic filters on the target (Green): Narrow down the data by filtering on attack patterns associated with the kill chain's initial access phase.

By leveraging these advanced filters, users can conduct detailed analyses within the Knowledge Graph perspective, unlocking insights that are crucial for understanding intricate relationships and statistics.

In certain views, you can access buttons like +, + Relationships, or + Entities. These buttons enable you to incorporate different data into the same widget for comparative analysis. For instance, in a Line view, adding a second set of filters will display two curves in the widget, each corresponding to one of the filtered data sets. Depending on the view, you can work with 1 to 5 sets of filters. The Label field allows you to name a data set, and this label can then be shown as a legend in the widget using the Display legend button in the widget parameters (see the next section).

"},{"location":"usage/widgets/#4-parameters","title":"4. Parameters","text":"

Parameters depend on the chosen visualization and allow users to define widget titles, choose displayed elements from the filtered data, select data reference date, and configure various other parameters specific to each visualization.

For the \"Knowledge Graph\" perspective, a critical parameter is the Display the source toggle. This feature empowers users to choose whether the widget displays entities from the source side or the target side of the relationships.

  • Toggle ON (\"Display the source\"): The widget focuses on entities positioned as the source of the relationships (in the \"from\" position).
  • Toggle OFF (\"Display the target\"): The widget shifts its focus to entities positioned as the target of the relationships (in the \"to\" position).

This level of control ensures that your dashboard aligns precisely with your analytical objectives, offering a tailored perspective based on your data and relationship.

"},{"location":"usage/widgets/#prerequisite-knowledge","title":"Prerequisite knowledge","text":"

To successfully configure widgets in OpenCTI, having a solid understanding of the platform's data modeling is essential. Knowing specific relationships, entities, and their attributes helps refine filters accurately. Let's explore two examples.

Scenarios 1:

Consider the scenario where you aim to visualize relationships between intrusion sets and attack patterns. In this case, the relevant relationship type connecting intrusion sets to attack patterns is labeled as \"Uses\" (as illustrated in the \"Filters\" section).

Scenarios 2:

Suppose your goal is to retrieve all reports associated with the finance sector. In this case, it's essential to use the correct filter for the finance sector. Instead of placing the finance sector in the \"Related entity\" filter, it should be placed in the \"Contains\" filter. Since a Report is a container object (like Cases and Groupings), it contains entities within it and is not related to entities.

"},{"location":"usage/widgets/#key-data-modeling-aspects","title":"Key data modeling aspects","text":"
  • Entities: Recognizing container (e.g. Reports, Cases and Groupings) and understanding the difference with non-container.
  • Relationships: Identifying the relationship types connecting entities.
  • Attributes: Understanding entities and relationships attributes for effective filtering.

Having this prerequisite knowledge allows you to navigate the widget configuration process seamlessly, ensuring accurate and insightful visualizations based on your specific data requirements.

"},{"location":"usage/workbench/","title":"Analyst workbench","text":"

Workbenches serve as dedicated workspaces for manipulating data before it is officially imported into the platform.

"},{"location":"usage/workbench/#location-of-use","title":"Location of use","text":"

The workbenches are located at various places within the platform:

"},{"location":"usage/workbench/#data-import-and-analyst-workbenches-window","title":"Data import and analyst workbenches window","text":"

This window encompasses all the necessary tools for importing a file. Files imported through this interface will subsequently be processed by the import connectors, resulting in the creation of workbenches. Additionally, analysts can manually create a workbench by clicking on the \"+\" icon at the bottom right of the window.

"},{"location":"usage/workbench/#data-tabs-of-all-entities","title":"Data tabs of all entities","text":"

Workbenches are also accessible through the \"Data\" tabs of entities, providing convenient access to import data associated with the entity.

"},{"location":"usage/workbench/#operation","title":"Operation","text":"

Workbenches are automatically generated upon the import of a file through an import connector. When an import connector is initiated, it scans files for recognizable entities and subsequently creates a workbench. All identified entities are placed within this workbench for analyst reviews. Alternatively, analysts have the option to manually create a workbench by clicking on the \"+\" icon at the bottom right of the \"Data import and analyst workbenches\" window.

The workbench being a draft space, the analysts use it to review connector proposals before finalizing them for import. Within the workbench, analysts have the flexibility to add, delete, or modify entities to meet specific requirements.

Once the content within the workbench is deemed acceptable, the analyst must initiate the ingestion process by clicking on Validate this workbench. This action signifies writing the data in the knowledge base.

Workbenches are drafting spaces

Until the workbench is validated, the contained data remains in draft form and is not recorded in the knowledge base. This ensures that only reviewed and approved data is officially integrated into the platform.

For more information on importing files, refer to the Import from files documentation page.

Confidence level of created knowledge through workbench

The confidence level of knowledge created through workbench is affected by the confidence level of the user. Please navigate to this page to understand in more details.

"},{"location":"usage/workflows/","title":"Workflows and assignation","text":"

Efficiently manage and organize your work within the OpenCTI platform by leveraging workflows and assignment. These capabilities provide a structured approach to tracking the status of objects and assigning responsibilities to users.

"},{"location":"usage/workflows/#workflows","title":"Workflows","text":"

Workflows are designed to trace the status of objects in the system. They are represented by the \"Processing status\" field embedded in each object. By default, this field is disabled for most objects but can be activated through the platform settings. For details on activating and configuring workflows, refer to the dedicated documentation page.

Enabling workflows enhances visibility into the progress and status of different objects, providing a comprehensive view for effective management.

"},{"location":"usage/workflows/#assignment-attributes","title":"Assignment attributes","text":"

Certain objects, including Reports, Cases, and Tasks, come equipped with \"Assignees\" and \"Participants\" attributes. These attributes serve the purpose of designating individuals responsible for the object and those who actively participate in it.

Attributes can be set as mandatory or with default values, streamlining the assignment process. Users can also be assigned or designated as participants manually, contributing to a collaborative and organized workflow. For details on configuring attributes, refer to the dedicated documentation page.

Users can stay informed about assignments through notification triggers. By setting up notification triggers, users receive alerts when an object is assigned to them. This ensures timely communication and proactive engagement with assigned tasks or responsibilities.

"}]} \ No newline at end of file diff --git a/6.2.X/sitemap.xml b/6.2.X/sitemap.xml new file mode 100755 index 00000000..34b644b8 --- /dev/null +++ b/6.2.X/sitemap.xml @@ -0,0 +1,458 @@ + + + + https://docs.opencti.io/latest/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/csv-mappers/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/decay-rules/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/enterprise/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/entities/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/file-indexing/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/introduction/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/merging/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/notifier-samples/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/notifiers/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/ontologies/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/organization-segregation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/parameters/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/policies/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/reasoning/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/retentions/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/segregation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/support-package/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/users/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/audit/configuration/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/audit/events/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/audit/overview/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/administration/audit/triggers/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/authentication/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/clustering/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/configuration/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/connectors/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/installation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/integrations/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/managers/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/map/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/overview/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/resources/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/rollover/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/troubleshooting/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/deployment/upgrade/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/api-usage/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/connectors/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/environment_ubuntu/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/environment_windows/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/platform/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/development/python/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/api/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/data-intelligence/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/data-model/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/filters-migration/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/filters/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/fips/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/streaming/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/taxonomy/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/reference/usage-telemetry/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/ask-ai/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/automation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/background-tasks/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/case-management/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/containers/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/dashboards-share/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/dashboards/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/data-model/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/dates/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/deduplication/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/delete-restore/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/enrichment/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-analysis/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-arsenal/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-cases/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-entities/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-events/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-locations/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-observations/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-techniques/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/exploring-threats/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/export/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/feeds/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/getting-started/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/import-automated/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/import-files/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/indicators-lifecycle/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/inferences/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/manual-creation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/merging/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/nested/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/notifications/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/overview/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/pivoting/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/reliability-confidence/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/search/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/tips-widget-creation/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/widgets/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/workbench/ + 2024-06-28 + daily + + + https://docs.opencti.io/latest/usage/workflows/ + 2024-06-28 + daily + + \ No newline at end of file diff --git a/6.2.X/sitemap.xml.gz b/6.2.X/sitemap.xml.gz new file mode 100755 index 00000000..88b00d6a Binary files /dev/null and b/6.2.X/sitemap.xml.gz differ diff --git a/6.2.X/usage/ask-ai/index.html b/6.2.X/usage/ask-ai/index.html new file mode 100755 index 00000000..e1a587f7 --- /dev/null +++ b/6.2.X/usage/ask-ai/index.html @@ -0,0 +1,5138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ask AI - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Ask AI

+
+

Enterprise edition

+

Ask AI is available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all information

+
+

Prerequisites for using Ask AI

+

There are several possibilities for Enterprise Edition customers to use OpenCTI AI endpoints:

+
    +
  • Use the Filigran AI Service leveraging our custom AI model using the token given by the support team.
  • +
  • Use OpenAI or MistralAI cloud endpoints using your own tokens.
  • +
  • Deploy or use local AI endpoints (Filigran can provide you with the custom model).
  • +
+

Please read the configuration documentation

+
+

Beta Feature

+

Ask AI is a beta feature as we are currently fine-tuning our models. Consider checking important information.

+
+

How it works

+

Even if in the future, we would like to leverage AI to do RAG, for the moment we are mostly using AI to analyze and produce texts or images, based on data directly sent into the prompt.

+

This means that if you are using Filigran AI endpoint or a local one, your data is never used to re-train or adapt the model and everything relies on a pre-trained and fixed model. When using the Ask AI button in the platform, a prompt is generated with the proper instruction to generate the expected result and use it in the context of the button (in forms, rich text editor etc.).

+

Filigran custom model

+

We are hosting a scalable AI endpoint for all SaaS or On-Prem enterprise edition customers, this endpoint is based on MistralAI with a model that will be adapted over time to be more effective when processing threat intelligence related contents.

+

The model, which is still in beta version, will be adapted in the upcoming months to reach maturity at the end of 2024. It can be shared with on-prem enterprise edition customers under NDA.

+

Functionalities of Ask AI

+

Ask AI is represented by a dedicated icon wherever on of its functionalities is available to use.

+

Create a new playbook

+

Assistance for writing meaningful content

+

Ask AI can assist you for writing better textual content, for example better title, name, description and detailed content of Objects.

+
    +
  • Fix spelling & grammar: try to improve the text from a formulation and grammar perspective.
  • +
  • Make it shorter/longer: try to shorten or lengthen the text.
  • +
  • Change tone: try to change the tone of the text. You can select if you want the text to be written for Strategic (Management, decision makers), Tactical (for team leaders) or Operational (for technical CTI analysts) audiences.
  • +
  • Summarize: try to summarize the text in bullet points.
  • +
  • Explain: try to explain the context of the subject's text based on what is available to the LLM.
  • +
+

Assistance for importing data from documents

+

Fom the Content tab of a Container (Reports, Groupings and Cases), Ask AI can also assist you for importing data contained in uploaded documents into OpenCTI for further exploitation.

+
    +
  • Generate report document: Generate a text report based on the knowledge graph (entities and relationships) of this container.
  • +
  • Summarize associated files: Generate a summary of the selected files (or all files associated to this container).
  • +
  • Try to convert the selected files (or all files associated to this container) in a STIX 2.1 bundle you will then be able to use at your convenience (for example importing it into the platform).
  • +
+

Generating report with Ask AI

+

Example of a generated content

+

A short video on the FiligranHQ YouTube channel presents tha capabilities of AskAI: https://www.youtube.com/watch?v=lsP3VVsk5ds.

+

Improving generated elements of Ask AI

+

Be aware that the text quality is highly dependent on the capabilities of the associated LLM.

+

That is why every generated text by Ask AI is provided in a dedicated panel, allowing you to verify and rectify any error the LLM could have made.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/assets/Generate_an_export_panel.png b/6.2.X/usage/assets/Generate_an_export_panel.png new file mode 100755 index 00000000..a9853f61 Binary files /dev/null and b/6.2.X/usage/assets/Generate_an_export_panel.png differ diff --git a/6.2.X/usage/assets/advanced-search.png b/6.2.X/usage/assets/advanced-search.png new file mode 100755 index 00000000..40affb97 Binary files /dev/null and b/6.2.X/usage/assets/advanced-search.png differ diff --git a/6.2.X/usage/assets/analyses-graphview.png b/6.2.X/usage/assets/analyses-graphview.png new file mode 100755 index 00000000..45315f60 Binary files /dev/null and b/6.2.X/usage/assets/analyses-graphview.png differ diff --git a/6.2.X/usage/assets/analysis-default-page.png b/6.2.X/usage/assets/analysis-default-page.png new file mode 100755 index 00000000..35e8bf52 Binary files /dev/null and b/6.2.X/usage/assets/analysis-default-page.png differ diff --git a/6.2.X/usage/assets/analysis.png b/6.2.X/usage/assets/analysis.png new file mode 100755 index 00000000..5316d447 Binary files /dev/null and b/6.2.X/usage/assets/analysis.png differ diff --git a/6.2.X/usage/assets/apt41_knowledge_view.png b/6.2.X/usage/assets/apt41_knowledge_view.png new file mode 100755 index 00000000..8c164b08 Binary files /dev/null and b/6.2.X/usage/assets/apt41_knowledge_view.png differ diff --git a/6.2.X/usage/assets/areas_list_view.png b/6.2.X/usage/assets/areas_list_view.png new file mode 100755 index 00000000..74b75508 Binary files /dev/null and b/6.2.X/usage/assets/areas_list_view.png differ diff --git a/6.2.X/usage/assets/artefact_overview.png b/6.2.X/usage/assets/artefact_overview.png new file mode 100755 index 00000000..c21af036 Binary files /dev/null and b/6.2.X/usage/assets/artefact_overview.png differ diff --git a/6.2.X/usage/assets/artefacts-list-view.png b/6.2.X/usage/assets/artefacts-list-view.png new file mode 100755 index 00000000..4c3fc16a Binary files /dev/null and b/6.2.X/usage/assets/artefacts-list-view.png differ diff --git a/6.2.X/usage/assets/askai_generatedcontent.png b/6.2.X/usage/assets/askai_generatedcontent.png new file mode 100755 index 00000000..927945fa Binary files /dev/null and b/6.2.X/usage/assets/askai_generatedcontent.png differ diff --git a/6.2.X/usage/assets/askai_generatereport.png b/6.2.X/usage/assets/askai_generatereport.png new file mode 100755 index 00000000..9f6b8627 Binary files /dev/null and b/6.2.X/usage/assets/askai_generatereport.png differ diff --git a/6.2.X/usage/assets/askai_icon.png b/6.2.X/usage/assets/askai_icon.png new file mode 100755 index 00000000..8bf19269 Binary files /dev/null and b/6.2.X/usage/assets/askai_icon.png differ diff --git a/6.2.X/usage/assets/assignment-attributes.png b/6.2.X/usage/assets/assignment-attributes.png new file mode 100755 index 00000000..65753437 Binary files /dev/null and b/6.2.X/usage/assets/assignment-attributes.png differ diff --git a/6.2.X/usage/assets/background-tasks.png b/6.2.X/usage/assets/background-tasks.png new file mode 100755 index 00000000..a882b4da Binary files /dev/null and b/6.2.X/usage/assets/background-tasks.png differ diff --git a/6.2.X/usage/assets/basic.png b/6.2.X/usage/assets/basic.png new file mode 100755 index 00000000..710e4284 Binary files /dev/null and b/6.2.X/usage/assets/basic.png differ diff --git a/6.2.X/usage/assets/bulk-result.png b/6.2.X/usage/assets/bulk-result.png new file mode 100755 index 00000000..f956c99a Binary files /dev/null and b/6.2.X/usage/assets/bulk-result.png differ diff --git a/6.2.X/usage/assets/bulk-search.png b/6.2.X/usage/assets/bulk-search.png new file mode 100755 index 00000000..82bdafd1 Binary files /dev/null and b/6.2.X/usage/assets/bulk-search.png differ diff --git a/6.2.X/usage/assets/campaigns-cards.png b/6.2.X/usage/assets/campaigns-cards.png new file mode 100755 index 00000000..13ef27e1 Binary files /dev/null and b/6.2.X/usage/assets/campaigns-cards.png differ diff --git a/6.2.X/usage/assets/cards-threat-group.png b/6.2.X/usage/assets/cards-threat-group.png new file mode 100755 index 00000000..991a6857 Binary files /dev/null and b/6.2.X/usage/assets/cards-threat-group.png differ diff --git a/6.2.X/usage/assets/case-applying-template.png b/6.2.X/usage/assets/case-applying-template.png new file mode 100755 index 00000000..5419b306 Binary files /dev/null and b/6.2.X/usage/assets/case-applying-template.png differ diff --git a/6.2.X/usage/assets/case-content-mapping.png b/6.2.X/usage/assets/case-content-mapping.png new file mode 100755 index 00000000..f8c37b1e Binary files /dev/null and b/6.2.X/usage/assets/case-content-mapping.png differ diff --git a/6.2.X/usage/assets/case-graph.png b/6.2.X/usage/assets/case-graph.png new file mode 100755 index 00000000..d38ad047 Binary files /dev/null and b/6.2.X/usage/assets/case-graph.png differ diff --git a/6.2.X/usage/assets/case-opinion.png b/6.2.X/usage/assets/case-opinion.png new file mode 100755 index 00000000..4994e0ca Binary files /dev/null and b/6.2.X/usage/assets/case-opinion.png differ diff --git a/6.2.X/usage/assets/case-timeline.png b/6.2.X/usage/assets/case-timeline.png new file mode 100755 index 00000000..f8240dc3 Binary files /dev/null and b/6.2.X/usage/assets/case-timeline.png differ diff --git a/6.2.X/usage/assets/cases-default-landing-page.png b/6.2.X/usage/assets/cases-default-landing-page.png new file mode 100755 index 00000000..0165f697 Binary files /dev/null and b/6.2.X/usage/assets/cases-default-landing-page.png differ diff --git a/6.2.X/usage/assets/cases-list.png b/6.2.X/usage/assets/cases-list.png new file mode 100755 index 00000000..668123d7 Binary files /dev/null and b/6.2.X/usage/assets/cases-list.png differ diff --git a/6.2.X/usage/assets/cases-observables.png b/6.2.X/usage/assets/cases-observables.png new file mode 100755 index 00000000..15c4b327 Binary files /dev/null and b/6.2.X/usage/assets/cases-observables.png differ diff --git a/6.2.X/usage/assets/channels_list_view.png b/6.2.X/usage/assets/channels_list_view.png new file mode 100755 index 00000000..e1d968cb Binary files /dev/null and b/6.2.X/usage/assets/channels_list_view.png differ diff --git a/6.2.X/usage/assets/cities_list_view.png b/6.2.X/usage/assets/cities_list_view.png new file mode 100755 index 00000000..ff2e5279 Binary files /dev/null and b/6.2.X/usage/assets/cities_list_view.png differ diff --git a/6.2.X/usage/assets/confidence-level-customization.png b/6.2.X/usage/assets/confidence-level-customization.png new file mode 100755 index 00000000..490b2957 Binary files /dev/null and b/6.2.X/usage/assets/confidence-level-customization.png differ diff --git a/6.2.X/usage/assets/container-filters.png b/6.2.X/usage/assets/container-filters.png new file mode 100755 index 00000000..a72dc54e Binary files /dev/null and b/6.2.X/usage/assets/container-filters.png differ diff --git a/6.2.X/usage/assets/contextual-search.png b/6.2.X/usage/assets/contextual-search.png new file mode 100755 index 00000000..7fbb2d53 Binary files /dev/null and b/6.2.X/usage/assets/contextual-search.png differ diff --git a/6.2.X/usage/assets/countries_list_view.png b/6.2.X/usage/assets/countries_list_view.png new file mode 100755 index 00000000..66789368 Binary files /dev/null and b/6.2.X/usage/assets/countries_list_view.png differ diff --git a/6.2.X/usage/assets/create-relationship.png b/6.2.X/usage/assets/create-relationship.png new file mode 100755 index 00000000..584416d5 Binary files /dev/null and b/6.2.X/usage/assets/create-relationship.png differ diff --git a/6.2.X/usage/assets/create-report.png b/6.2.X/usage/assets/create-report.png new file mode 100755 index 00000000..29ecc044 Binary files /dev/null and b/6.2.X/usage/assets/create-report.png differ diff --git a/6.2.X/usage/assets/csv-feed.png b/6.2.X/usage/assets/csv-feed.png new file mode 100755 index 00000000..74ca862f Binary files /dev/null and b/6.2.X/usage/assets/csv-feed.png differ diff --git a/6.2.X/usage/assets/csv-feeds-connectors.png b/6.2.X/usage/assets/csv-feeds-connectors.png new file mode 100755 index 00000000..0ca51e31 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-connectors.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-after-test.png b/6.2.X/usage/assets/csv-feeds-creation-after-test.png new file mode 100755 index 00000000..ea045078 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-after-test.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-csv-mapper.png b/6.2.X/usage/assets/csv-feeds-creation-csv-mapper.png new file mode 100755 index 00000000..cd65c45d Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-csv-mapper.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-list.png b/6.2.X/usage/assets/csv-feeds-creation-list.png new file mode 100755 index 00000000..af3bff31 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-list.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-prior-test.png b/6.2.X/usage/assets/csv-feeds-creation-prior-test.png new file mode 100755 index 00000000..2ed14ac9 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-prior-test.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-start.png b/6.2.X/usage/assets/csv-feeds-creation-start.png new file mode 100755 index 00000000..0d83db08 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-start.png differ diff --git a/6.2.X/usage/assets/csv-feeds-creation-test.png b/6.2.X/usage/assets/csv-feeds-creation-test.png new file mode 100755 index 00000000..db29af05 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-creation-test.png differ diff --git a/6.2.X/usage/assets/csv-feeds-importCSV-connector-tracking.png b/6.2.X/usage/assets/csv-feeds-importCSV-connector-tracking.png new file mode 100755 index 00000000..6e49c6c6 Binary files /dev/null and b/6.2.X/usage/assets/csv-feeds-importCSV-connector-tracking.png differ diff --git a/6.2.X/usage/assets/custom-notifiers.png b/6.2.X/usage/assets/custom-notifiers.png new file mode 100755 index 00000000..d9664c24 Binary files /dev/null and b/6.2.X/usage/assets/custom-notifiers.png differ diff --git a/6.2.X/usage/assets/customize_your_export.png b/6.2.X/usage/assets/customize_your_export.png new file mode 100755 index 00000000..465ad961 Binary files /dev/null and b/6.2.X/usage/assets/customize_your_export.png differ diff --git a/6.2.X/usage/assets/dashboard-overview.png b/6.2.X/usage/assets/dashboard-overview.png new file mode 100755 index 00000000..ebff2899 Binary files /dev/null and b/6.2.X/usage/assets/dashboard-overview.png differ diff --git a/6.2.X/usage/assets/dashboard.png b/6.2.X/usage/assets/dashboard.png new file mode 100755 index 00000000..c984fffb Binary files /dev/null and b/6.2.X/usage/assets/dashboard.png differ diff --git a/6.2.X/usage/assets/data-import-and-workbenches.png b/6.2.X/usage/assets/data-import-and-workbenches.png new file mode 100755 index 00000000..84f76a9e Binary files /dev/null and b/6.2.X/usage/assets/data-import-and-workbenches.png differ diff --git a/6.2.X/usage/assets/dates.png b/6.2.X/usage/assets/dates.png new file mode 100755 index 00000000..f1a0801b Binary files /dev/null and b/6.2.X/usage/assets/dates.png differ diff --git a/6.2.X/usage/assets/details.png b/6.2.X/usage/assets/details.png new file mode 100755 index 00000000..f48d2d3f Binary files /dev/null and b/6.2.X/usage/assets/details.png differ diff --git a/6.2.X/usage/assets/digest-creation.png b/6.2.X/usage/assets/digest-creation.png new file mode 100755 index 00000000..617232a2 Binary files /dev/null and b/6.2.X/usage/assets/digest-creation.png differ diff --git a/6.2.X/usage/assets/duplicate-dashboard-button.png b/6.2.X/usage/assets/duplicate-dashboard-button.png new file mode 100755 index 00000000..c1400b66 Binary files /dev/null and b/6.2.X/usage/assets/duplicate-dashboard-button.png differ diff --git a/6.2.X/usage/assets/duplicate-dashboard-success-message.png b/6.2.X/usage/assets/duplicate-dashboard-success-message.png new file mode 100755 index 00000000..c799021d Binary files /dev/null and b/6.2.X/usage/assets/duplicate-dashboard-success-message.png differ diff --git a/6.2.X/usage/assets/enrichment-button.png b/6.2.X/usage/assets/enrichment-button.png new file mode 100755 index 00000000..cd488084 Binary files /dev/null and b/6.2.X/usage/assets/enrichment-button.png differ diff --git a/6.2.X/usage/assets/enrichment-panel.png b/6.2.X/usage/assets/enrichment-panel.png new file mode 100755 index 00000000..479f999d Binary files /dev/null and b/6.2.X/usage/assets/enrichment-panel.png differ diff --git a/6.2.X/usage/assets/entities-to-link.png b/6.2.X/usage/assets/entities-to-link.png new file mode 100755 index 00000000..d5398de3 Binary files /dev/null and b/6.2.X/usage/assets/entities-to-link.png differ diff --git a/6.2.X/usage/assets/entities.png b/6.2.X/usage/assets/entities.png new file mode 100755 index 00000000..ab1da1bb Binary files /dev/null and b/6.2.X/usage/assets/entities.png differ diff --git a/6.2.X/usage/assets/entity-content-tab.png b/6.2.X/usage/assets/entity-content-tab.png new file mode 100755 index 00000000..42054d47 Binary files /dev/null and b/6.2.X/usage/assets/entity-content-tab.png differ diff --git a/6.2.X/usage/assets/entity-data-tab.png b/6.2.X/usage/assets/entity-data-tab.png new file mode 100755 index 00000000..9865587e Binary files /dev/null and b/6.2.X/usage/assets/entity-data-tab.png differ diff --git a/6.2.X/usage/assets/entity-reliability-confidence.png b/6.2.X/usage/assets/entity-reliability-confidence.png new file mode 100755 index 00000000..03e3b5a6 Binary files /dev/null and b/6.2.X/usage/assets/entity-reliability-confidence.png differ diff --git a/6.2.X/usage/assets/entity_analysis-tab.png b/6.2.X/usage/assets/entity_analysis-tab.png new file mode 100755 index 00000000..68f2a2fe Binary files /dev/null and b/6.2.X/usage/assets/entity_analysis-tab.png differ diff --git a/6.2.X/usage/assets/events_list_view.png b/6.2.X/usage/assets/events_list_view.png new file mode 100755 index 00000000..02bdd0f7 Binary files /dev/null and b/6.2.X/usage/assets/events_list_view.png differ diff --git a/6.2.X/usage/assets/example-admiralty-code.png b/6.2.X/usage/assets/example-admiralty-code.png new file mode 100755 index 00000000..17e0aae0 Binary files /dev/null and b/6.2.X/usage/assets/example-admiralty-code.png differ diff --git a/6.2.X/usage/assets/example-objective-scale.png b/6.2.X/usage/assets/example-objective-scale.png new file mode 100755 index 00000000..6a635984 Binary files /dev/null and b/6.2.X/usage/assets/example-objective-scale.png differ diff --git a/6.2.X/usage/assets/export-dashboard-option.png b/6.2.X/usage/assets/export-dashboard-option.png new file mode 100755 index 00000000..f8dac700 Binary files /dev/null and b/6.2.X/usage/assets/export-dashboard-option.png differ diff --git a/6.2.X/usage/assets/export-json-button.png b/6.2.X/usage/assets/export-json-button.png new file mode 100755 index 00000000..86eb6813 Binary files /dev/null and b/6.2.X/usage/assets/export-json-button.png differ diff --git a/6.2.X/usage/assets/export-widget-option.png b/6.2.X/usage/assets/export-widget-option.png new file mode 100755 index 00000000..69aa3be0 Binary files /dev/null and b/6.2.X/usage/assets/export-widget-option.png differ diff --git a/6.2.X/usage/assets/export_entity_content.png b/6.2.X/usage/assets/export_entity_content.png new file mode 100755 index 00000000..cfe70e19 Binary files /dev/null and b/6.2.X/usage/assets/export_entity_content.png differ diff --git a/6.2.X/usage/assets/export_specific_elements.png b/6.2.X/usage/assets/export_specific_elements.png new file mode 100755 index 00000000..502eebda Binary files /dev/null and b/6.2.X/usage/assets/export_specific_elements.png differ diff --git a/6.2.X/usage/assets/export_types.png b/6.2.X/usage/assets/export_types.png new file mode 100755 index 00000000..bde8a3c8 Binary files /dev/null and b/6.2.X/usage/assets/export_types.png differ diff --git a/6.2.X/usage/assets/exported-files-list.png b/6.2.X/usage/assets/exported-files-list.png new file mode 100755 index 00000000..60a046f2 Binary files /dev/null and b/6.2.X/usage/assets/exported-files-list.png differ diff --git a/6.2.X/usage/assets/exports_list.png b/6.2.X/usage/assets/exports_list.png new file mode 100755 index 00000000..698c36d9 Binary files /dev/null and b/6.2.X/usage/assets/exports_list.png differ diff --git a/6.2.X/usage/assets/feed-access-restriction.png b/6.2.X/usage/assets/feed-access-restriction.png new file mode 100755 index 00000000..839f7450 Binary files /dev/null and b/6.2.X/usage/assets/feed-access-restriction.png differ diff --git a/6.2.X/usage/assets/feedback-overview.png b/6.2.X/usage/assets/feedback-overview.png new file mode 100755 index 00000000..e094fe64 Binary files /dev/null and b/6.2.X/usage/assets/feedback-overview.png differ diff --git a/6.2.X/usage/assets/feedbacks_list.png b/6.2.X/usage/assets/feedbacks_list.png new file mode 100755 index 00000000..e483cc49 Binary files /dev/null and b/6.2.X/usage/assets/feedbacks_list.png differ diff --git a/6.2.X/usage/assets/filter-source-reliability.png b/6.2.X/usage/assets/filter-source-reliability.png new file mode 100755 index 00000000..74059a2e Binary files /dev/null and b/6.2.X/usage/assets/filter-source-reliability.png differ diff --git a/6.2.X/usage/assets/filter-to-delete.png b/6.2.X/usage/assets/filter-to-delete.png new file mode 100755 index 00000000..98989c2e Binary files /dev/null and b/6.2.X/usage/assets/filter-to-delete.png differ diff --git a/6.2.X/usage/assets/global-import-panel.png b/6.2.X/usage/assets/global-import-panel.png new file mode 100755 index 00000000..64923566 Binary files /dev/null and b/6.2.X/usage/assets/global-import-panel.png differ diff --git a/6.2.X/usage/assets/global-search-files.png b/6.2.X/usage/assets/global-search-files.png new file mode 100755 index 00000000..67cd5bd4 Binary files /dev/null and b/6.2.X/usage/assets/global-search-files.png differ diff --git a/6.2.X/usage/assets/graph.png b/6.2.X/usage/assets/graph.png new file mode 100755 index 00000000..f19549fb Binary files /dev/null and b/6.2.X/usage/assets/graph.png differ diff --git a/6.2.X/usage/assets/group-confidence-overrides.png b/6.2.X/usage/assets/group-confidence-overrides.png new file mode 100755 index 00000000..5ffe719c Binary files /dev/null and b/6.2.X/usage/assets/group-confidence-overrides.png differ diff --git a/6.2.X/usage/assets/hide-global.png b/6.2.X/usage/assets/hide-global.png new file mode 100755 index 00000000..5cf22487 Binary files /dev/null and b/6.2.X/usage/assets/hide-global.png differ diff --git a/6.2.X/usage/assets/hide-roles.png b/6.2.X/usage/assets/hide-roles.png new file mode 100755 index 00000000..a99276a6 Binary files /dev/null and b/6.2.X/usage/assets/hide-roles.png differ diff --git a/6.2.X/usage/assets/history.png b/6.2.X/usage/assets/history.png new file mode 100755 index 00000000..20034bd6 Binary files /dev/null and b/6.2.X/usage/assets/history.png differ diff --git a/6.2.X/usage/assets/import-dashboard-option.png b/6.2.X/usage/assets/import-dashboard-option.png new file mode 100755 index 00000000..4f20a9b4 Binary files /dev/null and b/6.2.X/usage/assets/import-dashboard-option.png differ diff --git a/6.2.X/usage/assets/import-widget-option.png b/6.2.X/usage/assets/import-widget-option.png new file mode 100755 index 00000000..68549c66 Binary files /dev/null and b/6.2.X/usage/assets/import-widget-option.png differ diff --git a/6.2.X/usage/assets/incident_overview.png b/6.2.X/usage/assets/incident_overview.png new file mode 100755 index 00000000..88a9fc4f Binary files /dev/null and b/6.2.X/usage/assets/incident_overview.png differ diff --git a/6.2.X/usage/assets/incidents_list_view.png b/6.2.X/usage/assets/incidents_list_view.png new file mode 100755 index 00000000..ca138d11 Binary files /dev/null and b/6.2.X/usage/assets/incidents_list_view.png differ diff --git a/6.2.X/usage/assets/indicator_overview.png b/6.2.X/usage/assets/indicator_overview.png new file mode 100755 index 00000000..c1a7ca60 Binary files /dev/null and b/6.2.X/usage/assets/indicator_overview.png differ diff --git a/6.2.X/usage/assets/indicators-lifecycle-example-dialog.png b/6.2.X/usage/assets/indicators-lifecycle-example-dialog.png new file mode 100755 index 00000000..48086bc0 Binary files /dev/null and b/6.2.X/usage/assets/indicators-lifecycle-example-dialog.png differ diff --git a/6.2.X/usage/assets/indicators-lifecycle-example-overview.png b/6.2.X/usage/assets/indicators-lifecycle-example-overview.png new file mode 100755 index 00000000..1839762f Binary files /dev/null and b/6.2.X/usage/assets/indicators-lifecycle-example-overview.png differ diff --git a/6.2.X/usage/assets/indicators-list-view.png b/6.2.X/usage/assets/indicators-list-view.png new file mode 100755 index 00000000..44eb2c55 Binary files /dev/null and b/6.2.X/usage/assets/indicators-list-view.png differ diff --git a/6.2.X/usage/assets/individuals_list_view.png b/6.2.X/usage/assets/individuals_list_view.png new file mode 100755 index 00000000..d19679a7 Binary files /dev/null and b/6.2.X/usage/assets/individuals_list_view.png differ diff --git a/6.2.X/usage/assets/inferred-rel-in-graph.png b/6.2.X/usage/assets/inferred-rel-in-graph.png new file mode 100755 index 00000000..3f13c489 Binary files /dev/null and b/6.2.X/usage/assets/inferred-rel-in-graph.png differ diff --git a/6.2.X/usage/assets/inferred-rel-in-list.png b/6.2.X/usage/assets/inferred-rel-in-list.png new file mode 100755 index 00000000..f62afb8f Binary files /dev/null and b/6.2.X/usage/assets/inferred-rel-in-list.png differ diff --git a/6.2.X/usage/assets/infrastructures-list-view.png b/6.2.X/usage/assets/infrastructures-list-view.png new file mode 100755 index 00000000..4b69700d Binary files /dev/null and b/6.2.X/usage/assets/infrastructures-list-view.png differ diff --git a/6.2.X/usage/assets/instance-trigger-creation.png b/6.2.X/usage/assets/instance-trigger-creation.png new file mode 100755 index 00000000..32e0c5a5 Binary files /dev/null and b/6.2.X/usage/assets/instance-trigger-creation.png differ diff --git a/6.2.X/usage/assets/instrusion-set-cards.png b/6.2.X/usage/assets/instrusion-set-cards.png new file mode 100755 index 00000000..7b56ab42 Binary files /dev/null and b/6.2.X/usage/assets/instrusion-set-cards.png differ diff --git a/6.2.X/usage/assets/intrusionset_knowledge_view.png b/6.2.X/usage/assets/intrusionset_knowledge_view.png new file mode 100755 index 00000000..3edc84ca Binary files /dev/null and b/6.2.X/usage/assets/intrusionset_knowledge_view.png differ diff --git a/6.2.X/usage/assets/investigation-bottom-right-menu.png b/6.2.X/usage/assets/investigation-bottom-right-menu.png new file mode 100755 index 00000000..affaab63 Binary files /dev/null and b/6.2.X/usage/assets/investigation-bottom-right-menu.png differ diff --git a/6.2.X/usage/assets/investigation-create-relationship.png b/6.2.X/usage/assets/investigation-create-relationship.png new file mode 100755 index 00000000..a77c08df Binary files /dev/null and b/6.2.X/usage/assets/investigation-create-relationship.png differ diff --git a/6.2.X/usage/assets/investigation-expand-entity.png b/6.2.X/usage/assets/investigation-expand-entity.png new file mode 100755 index 00000000..8428cf10 Binary files /dev/null and b/6.2.X/usage/assets/investigation-expand-entity.png differ diff --git a/6.2.X/usage/assets/investigation-export.png b/6.2.X/usage/assets/investigation-export.png new file mode 100755 index 00000000..143d84bf Binary files /dev/null and b/6.2.X/usage/assets/investigation-export.png differ diff --git a/6.2.X/usage/assets/investigation-rollback-popup.png b/6.2.X/usage/assets/investigation-rollback-popup.png new file mode 100755 index 00000000..38d5e2a6 Binary files /dev/null and b/6.2.X/usage/assets/investigation-rollback-popup.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection-add.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection-add.png new file mode 100755 index 00000000..9a0b89a6 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection-add.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection.png new file mode 100755 index 00000000..1cbffba1 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-entity-selection.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity-form.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity-form.png new file mode 100755 index 00000000..5a805d06 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity-form.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity.png new file mode 100755 index 00000000..c1c82735 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case-dialog-new-entity.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case-success.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case-success.png new file mode 100755 index 00000000..7e1f54c4 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case-success.png differ diff --git a/6.2.X/usage/assets/investigation-turn-to-report-or-case.png b/6.2.X/usage/assets/investigation-turn-to-report-or-case.png new file mode 100755 index 00000000..af349281 Binary files /dev/null and b/6.2.X/usage/assets/investigation-turn-to-report-or-case.png differ diff --git a/6.2.X/usage/assets/investigation-workspace.png b/6.2.X/usage/assets/investigation-workspace.png new file mode 100755 index 00000000..8b725128 Binary files /dev/null and b/6.2.X/usage/assets/investigation-workspace.png differ diff --git a/6.2.X/usage/assets/investigation.png b/6.2.X/usage/assets/investigation.png new file mode 100755 index 00000000..fde57825 Binary files /dev/null and b/6.2.X/usage/assets/investigation.png differ diff --git a/6.2.X/usage/assets/knowledge_focus_indicators_observable_views.png b/6.2.X/usage/assets/knowledge_focus_indicators_observable_views.png new file mode 100755 index 00000000..d140c614 Binary files /dev/null and b/6.2.X/usage/assets/knowledge_focus_indicators_observable_views.png differ diff --git a/6.2.X/usage/assets/latest_additions.png b/6.2.X/usage/assets/latest_additions.png new file mode 100755 index 00000000..06ef3157 Binary files /dev/null and b/6.2.X/usage/assets/latest_additions.png differ diff --git a/6.2.X/usage/assets/live-stream-additional-configuration.png b/6.2.X/usage/assets/live-stream-additional-configuration.png new file mode 100755 index 00000000..d4b526e0 Binary files /dev/null and b/6.2.X/usage/assets/live-stream-additional-configuration.png differ diff --git a/6.2.X/usage/assets/live-stream-configuration.png b/6.2.X/usage/assets/live-stream-configuration.png new file mode 100755 index 00000000..ef7b7031 Binary files /dev/null and b/6.2.X/usage/assets/live-stream-configuration.png differ diff --git a/6.2.X/usage/assets/live-stream.png b/6.2.X/usage/assets/live-stream.png new file mode 100755 index 00000000..17fb2f0b Binary files /dev/null and b/6.2.X/usage/assets/live-stream.png differ diff --git a/6.2.X/usage/assets/magic-wand-icon.png b/6.2.X/usage/assets/magic-wand-icon.png new file mode 100755 index 00000000..703b04b9 Binary files /dev/null and b/6.2.X/usage/assets/magic-wand-icon.png differ diff --git a/6.2.X/usage/assets/malware_cards_view.png b/6.2.X/usage/assets/malware_cards_view.png new file mode 100755 index 00000000..f932b4e9 Binary files /dev/null and b/6.2.X/usage/assets/malware_cards_view.png differ diff --git a/6.2.X/usage/assets/malwareanalysis-overview.png b/6.2.X/usage/assets/malwareanalysis-overview.png new file mode 100755 index 00000000..a33003f2 Binary files /dev/null and b/6.2.X/usage/assets/malwareanalysis-overview.png differ diff --git a/6.2.X/usage/assets/manage-access-button.png b/6.2.X/usage/assets/manage-access-button.png new file mode 100755 index 00000000..54ef6bd5 Binary files /dev/null and b/6.2.X/usage/assets/manage-access-button.png differ diff --git a/6.2.X/usage/assets/manage-access-dialog.png b/6.2.X/usage/assets/manage-access-dialog.png new file mode 100755 index 00000000..0199d533 Binary files /dev/null and b/6.2.X/usage/assets/manage-access-dialog.png differ diff --git a/6.2.X/usage/assets/manual-import-connectors.png b/6.2.X/usage/assets/manual-import-connectors.png new file mode 100755 index 00000000..5edc62e8 Binary files /dev/null and b/6.2.X/usage/assets/manual-import-connectors.png differ diff --git a/6.2.X/usage/assets/menu.png b/6.2.X/usage/assets/menu.png new file mode 100755 index 00000000..4784d07b Binary files /dev/null and b/6.2.X/usage/assets/menu.png differ diff --git a/6.2.X/usage/assets/new-relationship.png b/6.2.X/usage/assets/new-relationship.png new file mode 100755 index 00000000..e1f93d39 Binary files /dev/null and b/6.2.X/usage/assets/new-relationship.png differ diff --git a/6.2.X/usage/assets/new-report.png b/6.2.X/usage/assets/new-report.png new file mode 100755 index 00000000..21d7f187 Binary files /dev/null and b/6.2.X/usage/assets/new-report.png differ diff --git a/6.2.X/usage/assets/notifications.png b/6.2.X/usage/assets/notifications.png new file mode 100755 index 00000000..a304c5d3 Binary files /dev/null and b/6.2.X/usage/assets/notifications.png differ diff --git a/6.2.X/usage/assets/notifiers.png b/6.2.X/usage/assets/notifiers.png new file mode 100755 index 00000000..706cf76a Binary files /dev/null and b/6.2.X/usage/assets/notifiers.png differ diff --git a/6.2.X/usage/assets/observable_overview.png b/6.2.X/usage/assets/observable_overview.png new file mode 100755 index 00000000..9b4cb6c4 Binary files /dev/null and b/6.2.X/usage/assets/observable_overview.png differ diff --git a/6.2.X/usage/assets/observables-list-view.png b/6.2.X/usage/assets/observables-list-view.png new file mode 100755 index 00000000..9a56e90d Binary files /dev/null and b/6.2.X/usage/assets/observables-list-view.png differ diff --git a/6.2.X/usage/assets/open_export_panel.png b/6.2.X/usage/assets/open_export_panel.png new file mode 100755 index 00000000..29ac497b Binary files /dev/null and b/6.2.X/usage/assets/open_export_panel.png differ diff --git a/6.2.X/usage/assets/organization_author_view.png b/6.2.X/usage/assets/organization_author_view.png new file mode 100755 index 00000000..bdca8d96 Binary files /dev/null and b/6.2.X/usage/assets/organization_author_view.png differ diff --git a/6.2.X/usage/assets/organizations_list_view.png b/6.2.X/usage/assets/organizations_list_view.png new file mode 100755 index 00000000..c35c5a6f Binary files /dev/null and b/6.2.X/usage/assets/organizations_list_view.png differ diff --git a/6.2.X/usage/assets/override-confidence-tooltip.png b/6.2.X/usage/assets/override-confidence-tooltip.png new file mode 100755 index 00000000..9b499f05 Binary files /dev/null and b/6.2.X/usage/assets/override-confidence-tooltip.png differ diff --git a/6.2.X/usage/assets/overview-of-workbench.png b/6.2.X/usage/assets/overview-of-workbench.png new file mode 100755 index 00000000..7f6d98b8 Binary files /dev/null and b/6.2.X/usage/assets/overview-of-workbench.png differ diff --git a/6.2.X/usage/assets/playbook_components.png b/6.2.X/usage/assets/playbook_components.png new file mode 100755 index 00000000..4c67de2f Binary files /dev/null and b/6.2.X/usage/assets/playbook_components.png differ diff --git a/6.2.X/usage/assets/playbook_create.png b/6.2.X/usage/assets/playbook_create.png new file mode 100755 index 00000000..6bf84480 Binary files /dev/null and b/6.2.X/usage/assets/playbook_create.png differ diff --git a/6.2.X/usage/assets/playbook_listen.png b/6.2.X/usage/assets/playbook_listen.png new file mode 100755 index 00000000..c68edb2e Binary files /dev/null and b/6.2.X/usage/assets/playbook_listen.png differ diff --git a/6.2.X/usage/assets/playbook_traces.png b/6.2.X/usage/assets/playbook_traces.png new file mode 100755 index 00000000..13d77d96 Binary files /dev/null and b/6.2.X/usage/assets/playbook_traces.png differ diff --git a/6.2.X/usage/assets/positions_list_view.png b/6.2.X/usage/assets/positions_list_view.png new file mode 100755 index 00000000..573a9536 Binary files /dev/null and b/6.2.X/usage/assets/positions_list_view.png differ diff --git a/6.2.X/usage/assets/processing-status.png b/6.2.X/usage/assets/processing-status.png new file mode 100755 index 00000000..76d01b9d Binary files /dev/null and b/6.2.X/usage/assets/processing-status.png differ diff --git a/6.2.X/usage/assets/profile_pictures_of_TA.png b/6.2.X/usage/assets/profile_pictures_of_TA.png new file mode 100755 index 00000000..51b15e31 Binary files /dev/null and b/6.2.X/usage/assets/profile_pictures_of_TA.png differ diff --git a/6.2.X/usage/assets/profile_pictures_panel.png b/6.2.X/usage/assets/profile_pictures_panel.png new file mode 100755 index 00000000..6f89ed38 Binary files /dev/null and b/6.2.X/usage/assets/profile_pictures_panel.png differ diff --git a/6.2.X/usage/assets/quick-subscription-button.png b/6.2.X/usage/assets/quick-subscription-button.png new file mode 100755 index 00000000..f15aefc5 Binary files /dev/null and b/6.2.X/usage/assets/quick-subscription-button.png differ diff --git a/6.2.X/usage/assets/ref_and_history.png b/6.2.X/usage/assets/ref_and_history.png new file mode 100755 index 00000000..a2bc0f14 Binary files /dev/null and b/6.2.X/usage/assets/ref_and_history.png differ diff --git a/6.2.X/usage/assets/region_overview.png b/6.2.X/usage/assets/region_overview.png new file mode 100755 index 00000000..8ee696ca Binary files /dev/null and b/6.2.X/usage/assets/region_overview.png differ diff --git a/6.2.X/usage/assets/regions_list_view.png b/6.2.X/usage/assets/regions_list_view.png new file mode 100755 index 00000000..44e9c1a6 Binary files /dev/null and b/6.2.X/usage/assets/regions_list_view.png differ diff --git a/6.2.X/usage/assets/relation-with-container.png b/6.2.X/usage/assets/relation-with-container.png new file mode 100755 index 00000000..5e08e2d5 Binary files /dev/null and b/6.2.X/usage/assets/relation-with-container.png differ diff --git a/6.2.X/usage/assets/report-content-mapping-view.png b/6.2.X/usage/assets/report-content-mapping-view.png new file mode 100755 index 00000000..14b65499 Binary files /dev/null and b/6.2.X/usage/assets/report-content-mapping-view.png differ diff --git a/6.2.X/usage/assets/report-correlation-view.png b/6.2.X/usage/assets/report-correlation-view.png new file mode 100755 index 00000000..85d03c7d Binary files /dev/null and b/6.2.X/usage/assets/report-correlation-view.png differ diff --git a/6.2.X/usage/assets/report-graph-view.png b/6.2.X/usage/assets/report-graph-view.png new file mode 100755 index 00000000..52e9d6be Binary files /dev/null and b/6.2.X/usage/assets/report-graph-view.png differ diff --git a/6.2.X/usage/assets/report-matrix-view.png b/6.2.X/usage/assets/report-matrix-view.png new file mode 100755 index 00000000..060b9956 Binary files /dev/null and b/6.2.X/usage/assets/report-matrix-view.png differ diff --git a/6.2.X/usage/assets/report-timeline-view.png b/6.2.X/usage/assets/report-timeline-view.png new file mode 100755 index 00000000..3b7ef7b6 Binary files /dev/null and b/6.2.X/usage/assets/report-timeline-view.png differ diff --git a/6.2.X/usage/assets/restricted-trigger.png b/6.2.X/usage/assets/restricted-trigger.png new file mode 100755 index 00000000..a8a8b2c2 Binary files /dev/null and b/6.2.X/usage/assets/restricted-trigger.png differ diff --git a/6.2.X/usage/assets/rss-feed-configuration.png b/6.2.X/usage/assets/rss-feed-configuration.png new file mode 100755 index 00000000..25e61fc2 Binary files /dev/null and b/6.2.X/usage/assets/rss-feed-configuration.png differ diff --git a/6.2.X/usage/assets/schema.png b/6.2.X/usage/assets/schema.png new file mode 100755 index 00000000..c1811586 Binary files /dev/null and b/6.2.X/usage/assets/schema.png differ diff --git a/6.2.X/usage/assets/search-bar.png b/6.2.X/usage/assets/search-bar.png new file mode 100755 index 00000000..1a05fbb2 Binary files /dev/null and b/6.2.X/usage/assets/search-bar.png differ diff --git a/6.2.X/usage/assets/search-filters.png b/6.2.X/usage/assets/search-filters.png new file mode 100755 index 00000000..597e6328 Binary files /dev/null and b/6.2.X/usage/assets/search-filters.png differ diff --git a/6.2.X/usage/assets/search-graph.png b/6.2.X/usage/assets/search-graph.png new file mode 100755 index 00000000..f1c026b3 Binary files /dev/null and b/6.2.X/usage/assets/search-graph.png differ diff --git a/6.2.X/usage/assets/sector_overview.png b/6.2.X/usage/assets/sector_overview.png new file mode 100755 index 00000000..68eac4a0 Binary files /dev/null and b/6.2.X/usage/assets/sector_overview.png differ diff --git a/6.2.X/usage/assets/sectors_list_view.png b/6.2.X/usage/assets/sectors_list_view.png new file mode 100755 index 00000000..f2f1c280 Binary files /dev/null and b/6.2.X/usage/assets/sectors_list_view.png differ diff --git a/6.2.X/usage/assets/settings-reliability_ov.png b/6.2.X/usage/assets/settings-reliability_ov.png new file mode 100755 index 00000000..2a3e4a3d Binary files /dev/null and b/6.2.X/usage/assets/settings-reliability_ov.png differ diff --git a/6.2.X/usage/assets/share-dashboard-button.png b/6.2.X/usage/assets/share-dashboard-button.png new file mode 100755 index 00000000..66487cf9 Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-button.png differ diff --git a/6.2.X/usage/assets/share-dashboard-enable.png b/6.2.X/usage/assets/share-dashboard-enable.png new file mode 100755 index 00000000..df5563b7 Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-enable.png differ diff --git a/6.2.X/usage/assets/share-dashboard-list.png b/6.2.X/usage/assets/share-dashboard-list.png new file mode 100755 index 00000000..37c05d5d Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-list.png differ diff --git a/6.2.X/usage/assets/share-dashboard-markings.png b/6.2.X/usage/assets/share-dashboard-markings.png new file mode 100755 index 00000000..ee4ca503 Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-markings.png differ diff --git a/6.2.X/usage/assets/share-dashboard-name.png b/6.2.X/usage/assets/share-dashboard-name.png new file mode 100755 index 00000000..ce545930 Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-name.png differ diff --git a/6.2.X/usage/assets/share-dashboard-panel.png b/6.2.X/usage/assets/share-dashboard-panel.png new file mode 100755 index 00000000..9ebbdc4e Binary files /dev/null and b/6.2.X/usage/assets/share-dashboard-panel.png differ diff --git a/6.2.X/usage/assets/sightings_list.png b/6.2.X/usage/assets/sightings_list.png new file mode 100755 index 00000000..fa5b878d Binary files /dev/null and b/6.2.X/usage/assets/sightings_list.png differ diff --git a/6.2.X/usage/assets/stix.png b/6.2.X/usage/assets/stix.png new file mode 100755 index 00000000..7496c65e Binary files /dev/null and b/6.2.X/usage/assets/stix.png differ diff --git a/6.2.X/usage/assets/systems_list_view.png b/6.2.X/usage/assets/systems_list_view.png new file mode 100755 index 00000000..a41af585 Binary files /dev/null and b/6.2.X/usage/assets/systems_list_view.png differ diff --git a/6.2.X/usage/assets/taxii-collection.png b/6.2.X/usage/assets/taxii-collection.png new file mode 100755 index 00000000..e4252371 Binary files /dev/null and b/6.2.X/usage/assets/taxii-collection.png differ diff --git a/6.2.X/usage/assets/taxii-feed-configuration.png b/6.2.X/usage/assets/taxii-feed-configuration.png new file mode 100755 index 00000000..8c115363 Binary files /dev/null and b/6.2.X/usage/assets/taxii-feed-configuration.png differ diff --git a/6.2.X/usage/assets/threat-actor-individual-biographics.png b/6.2.X/usage/assets/threat-actor-individual-biographics.png new file mode 100755 index 00000000..779e05bb Binary files /dev/null and b/6.2.X/usage/assets/threat-actor-individual-biographics.png differ diff --git a/6.2.X/usage/assets/threat-actor-individual-demographics.png b/6.2.X/usage/assets/threat-actor-individual-demographics.png new file mode 100755 index 00000000..450e0b20 Binary files /dev/null and b/6.2.X/usage/assets/threat-actor-individual-demographics.png differ diff --git a/6.2.X/usage/assets/tools_list_view.png b/6.2.X/usage/assets/tools_list_view.png new file mode 100755 index 00000000..7bf1881c Binary files /dev/null and b/6.2.X/usage/assets/tools_list_view.png differ diff --git a/6.2.X/usage/assets/top-menu-investigation.png b/6.2.X/usage/assets/top-menu-investigation.png new file mode 100755 index 00000000..d923b589 Binary files /dev/null and b/6.2.X/usage/assets/top-menu-investigation.png differ diff --git a/6.2.X/usage/assets/top-menu.png b/6.2.X/usage/assets/top-menu.png new file mode 100755 index 00000000..d136f06b Binary files /dev/null and b/6.2.X/usage/assets/top-menu.png differ diff --git a/6.2.X/usage/assets/trash-actions.png b/6.2.X/usage/assets/trash-actions.png new file mode 100755 index 00000000..7f370c2c Binary files /dev/null and b/6.2.X/usage/assets/trash-actions.png differ diff --git a/6.2.X/usage/assets/trash-delete-confirm.png b/6.2.X/usage/assets/trash-delete-confirm.png new file mode 100755 index 00000000..3dbcfa06 Binary files /dev/null and b/6.2.X/usage/assets/trash-delete-confirm.png differ diff --git a/6.2.X/usage/assets/trash-error-dependency-in-trash.png b/6.2.X/usage/assets/trash-error-dependency-in-trash.png new file mode 100755 index 00000000..6ada8a0b Binary files /dev/null and b/6.2.X/usage/assets/trash-error-dependency-in-trash.png differ diff --git a/6.2.X/usage/assets/trash-error-dependency-missing.png b/6.2.X/usage/assets/trash-error-dependency-missing.png new file mode 100755 index 00000000..d2b674e8 Binary files /dev/null and b/6.2.X/usage/assets/trash-error-dependency-missing.png differ diff --git a/6.2.X/usage/assets/trash-restore-confirm.png b/6.2.X/usage/assets/trash-restore-confirm.png new file mode 100755 index 00000000..22507032 Binary files /dev/null and b/6.2.X/usage/assets/trash-restore-confirm.png differ diff --git a/6.2.X/usage/assets/trash.png b/6.2.X/usage/assets/trash.png new file mode 100755 index 00000000..d15a890f Binary files /dev/null and b/6.2.X/usage/assets/trash.png differ diff --git a/6.2.X/usage/assets/trigger-configuration.png b/6.2.X/usage/assets/trigger-configuration.png new file mode 100755 index 00000000..ffebcddc Binary files /dev/null and b/6.2.X/usage/assets/trigger-configuration.png differ diff --git a/6.2.X/usage/assets/trigger_notifier_configuration.png b/6.2.X/usage/assets/trigger_notifier_configuration.png new file mode 100755 index 00000000..60435ddc Binary files /dev/null and b/6.2.X/usage/assets/trigger_notifier_configuration.png differ diff --git a/6.2.X/usage/assets/triggers.png b/6.2.X/usage/assets/triggers.png new file mode 100755 index 00000000..c0e263cd Binary files /dev/null and b/6.2.X/usage/assets/triggers.png differ diff --git a/6.2.X/usage/assets/update-confidence-level.png b/6.2.X/usage/assets/update-confidence-level.png new file mode 100755 index 00000000..065c1db7 Binary files /dev/null and b/6.2.X/usage/assets/update-confidence-level.png differ diff --git a/6.2.X/usage/assets/use-cases.png b/6.2.X/usage/assets/use-cases.png new file mode 100755 index 00000000..baf2a573 Binary files /dev/null and b/6.2.X/usage/assets/use-cases.png differ diff --git a/6.2.X/usage/assets/user-confidence-overrides.png b/6.2.X/usage/assets/user-confidence-overrides.png new file mode 100755 index 00000000..47d1ea15 Binary files /dev/null and b/6.2.X/usage/assets/user-confidence-overrides.png differ diff --git a/6.2.X/usage/assets/vulnerabilities_list_view.png b/6.2.X/usage/assets/vulnerabilities_list_view.png new file mode 100755 index 00000000..cac87a98 Binary files /dev/null and b/6.2.X/usage/assets/vulnerabilities_list_view.png differ diff --git a/6.2.X/usage/assets/widget-breakdown-by-entity-final.png b/6.2.X/usage/assets/widget-breakdown-by-entity-final.png new file mode 100755 index 00000000..277152fd Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdown-by-entity-final.png differ diff --git a/6.2.X/usage/assets/widget-breakdown-by-entity-parameter.png b/6.2.X/usage/assets/widget-breakdown-by-entity-parameter.png new file mode 100755 index 00000000..c58ebe0b Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdown-by-entity-parameter.png differ diff --git a/6.2.X/usage/assets/widget-breakdown-by-relation-filter.png b/6.2.X/usage/assets/widget-breakdown-by-relation-filter.png new file mode 100755 index 00000000..b25e958d Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdown-by-relation-filter.png differ diff --git a/6.2.X/usage/assets/widget-breakdown-by-relation-final.png b/6.2.X/usage/assets/widget-breakdown-by-relation-final.png new file mode 100755 index 00000000..5a8e2faf Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdown-by-relation-final.png differ diff --git a/6.2.X/usage/assets/widget-breakdown-example.png b/6.2.X/usage/assets/widget-breakdown-example.png new file mode 100755 index 00000000..236c4569 Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdown-example.png differ diff --git a/6.2.X/usage/assets/widget-breakdwon-by-entity-filter.png b/6.2.X/usage/assets/widget-breakdwon-by-entity-filter.png new file mode 100755 index 00000000..7021ff90 Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdwon-by-entity-filter.png differ diff --git a/6.2.X/usage/assets/widget-breakdwon-by-relation-parameter.png b/6.2.X/usage/assets/widget-breakdwon-by-relation-parameter.png new file mode 100755 index 00000000..974c6315 Binary files /dev/null and b/6.2.X/usage/assets/widget-breakdwon-by-relation-parameter.png differ diff --git a/6.2.X/usage/assets/widget-filters.png b/6.2.X/usage/assets/widget-filters.png new file mode 100755 index 00000000..7dbe3852 Binary files /dev/null and b/6.2.X/usage/assets/widget-filters.png differ diff --git a/6.2.X/usage/assets/widget-multiple-filters.png b/6.2.X/usage/assets/widget-multiple-filters.png new file mode 100755 index 00000000..2bbaa1c0 Binary files /dev/null and b/6.2.X/usage/assets/widget-multiple-filters.png differ diff --git a/6.2.X/usage/assets/widget-parameters.png b/6.2.X/usage/assets/widget-parameters.png new file mode 100755 index 00000000..5a72aaa6 Binary files /dev/null and b/6.2.X/usage/assets/widget-parameters.png differ diff --git a/6.2.X/usage/assets/widget-perspective.png b/6.2.X/usage/assets/widget-perspective.png new file mode 100755 index 00000000..9a2669de Binary files /dev/null and b/6.2.X/usage/assets/widget-perspective.png differ diff --git a/6.2.X/usage/assets/widget-visualization.png b/6.2.X/usage/assets/widget-visualization.png new file mode 100755 index 00000000..c1e6bbc8 Binary files /dev/null and b/6.2.X/usage/assets/widget-visualization.png differ diff --git a/6.2.X/usage/assets/workbench-data-manipulation.png b/6.2.X/usage/assets/workbench-data-manipulation.png new file mode 100755 index 00000000..cfd332c0 Binary files /dev/null and b/6.2.X/usage/assets/workbench-data-manipulation.png differ diff --git a/6.2.X/usage/assets/workbench-in-data-tab.png b/6.2.X/usage/assets/workbench-in-data-tab.png new file mode 100755 index 00000000..f4861a23 Binary files /dev/null and b/6.2.X/usage/assets/workbench-in-data-tab.png differ diff --git a/6.2.X/usage/automation/index.html b/6.2.X/usage/automation/index.html new file mode 100755 index 00000000..b56694de --- /dev/null +++ b/6.2.X/usage/automation/index.html @@ -0,0 +1,5379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Automation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Playbooks Automation

+
+

Enterprise edition

+

Playbooks automation is available under the "OpenCTI Enterprise Edition" license. Please read the dedicated page to have all information.

+
+

OpenCTI playbooks are flexible automation scenarios which can be fully customized and enabled by platform administrators to enrich, filter and modify the data created or updated in the platform.

+

Playbook automation is accessible in the user interface under Data > Processing > Automation.

+
+

Right needed

+

You need the "Manage credentials" capability to use the Playbooks automation, because you will be able to manipulate data simple users cannot access.

+
+

You will then be able to:

+
    +
  • add labels depending on enrichment results to be used in threat intelligence-driven detection feeds,
  • +
  • create reports and cases based on various criteria,
  • +
  • trigger enrichments or webhooks in given conditions,
  • +
  • modify attributes such as first_seen and last_seen based on other pieces of knowledge,
  • +
  • etc.
  • +
+

Playbook philosophy

+

Consider Playbook as a STIX 2.1 bundle pipeline.

+

Initiating with a component listening to a data stream, each subsequent component in the playbook processes a received STIX bundle. These components have the ability to modify the bundle and subsequently transmit the altered result to connected components.

+

In this paradigm, components can send out the STIX 2.1 bundle to multiple components, enabling the development of multiple branches within your playbook.

+

A well-designed playbook end with a component executing an action based on the processed information. For instance, this may involve writing the STIX 2.1 bundle in a data stream.

+
+

Validate ingestion

+

The STIX bundle processed by the playbook won't be written in the platform without specifying it using the appropriate component, i.e. "Send for ingestion".

+
+

Create a Playbook

+

It is possible to create as many playbooks as needed which are running independently. You can give a name and description to each playbook.

+

Create a new playbook

+

The first step to define in the playbook is the “triggering event”, which can be any knowledge event (create, update or delete) with customizable filters. To do so, click on the grey rectangle in the center of the workspace and choose the component to "listen knowledge events". Configure it with adequate filters. You can use same filters as in other part of the platform.

+

Listening creation event for TLP:GREEN IPs and domain names

+

Then you have flexible choices for the next steps to:

+
    +
  • filter the initial knowledge,
  • +
  • enrich data using external sources and internal rules,
  • +
  • modify entities and relationships by applying patches,
  • +
  • write the data, send notifications,
  • +
  • etc.
  • +
+

Do not forget to start your Playbook when ready, with the Start option of the burger button placed near the name of your Playbook.

+

By clicking the burger button of a component, you can replace it by another one.

+

By clicking on the arrow icon in the bottom right corner of a component, you can develop a new branch at the same level.

+

By clicking the "+" button on a link between components, you can insert a component between the two.

+

Components of playbooks

+

List of current components

+

Log data in standard output

+

Will write the received STIX 2.1 bundle in platform logs with configurable log level and then send out the STIX 2.1 bundle unmodified.

+

Send for ingestion

+

Will pass the STIX 2.1 bundle to be written in the data stream. This component has no output and should end a branch of your playbook.

+

Filter Knowledge

+

Will allow you to define filter and apply it to the received STIX 2.1 bundle. The component has 2 output, one for data matching the filter and one for the remainder.

+

By default, filtering is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

+

Enrich through connector

+

Will send the received STIX 2.1 bundle to a compatible enrichment connector and send out the modified bundle.

+

Manipulate knowledge

+

Will add, replace or remove compatible attribute of the entities contains in the received STIX 2.1 bundle and send out the modified bundle.

+

By default, modification is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

+

Container wrapper

+

Will modify the received STIX 2.1 bundle to include the entities into an container of the type you configured. +By default, wrapping is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all elements in the bundle (elements that might result from enrichment for example).

+

Share with organizations

+

Will share every entity in the received STIX 2.1 bundle with Organizations you configured. Your platform needs to have declared a platform main organization in Settings/Parameters.

+

Apply predefined rule

+

Will apply a complex automation built-in rule. This kind of rule might impact performance. Current rules are:

+
    +
  • First/Last seen computing extension from report publication date: will populate first seen and last seen date of entities contained in the report based on its publication date,
  • +
  • Resolve indicators based on observables (add in bundle): will retrieve all indicators linked to the bundle's observables from the database,
  • +
  • Resolve observables an indicator is based on (add in bundle): retrieve all observables linked to the bundle's indicator from the database,
  • +
  • Resolve container references (add in bundle): will add to the bundle all the relationships and entities the container contains (if the entity having triggered the playbook is not a container, the output of this component will be empty),
  • +
  • Resolve neighbors relations and entities (add in bundle): will add to the bundle all relations of the entity having triggered the playbook, as well as all entities at the end of these relations, i.e. the "first neighbors" (if the entity is a container, the output of this component will be empty).
  • +
+

Send to notifier

+

Will generate a Notification each time a STIX 2.1 bundle is received.

+

Promote observable to indicator

+

Will generate indicator based on observables contained in the received STIX 2.1 bundle.

+

By default, it is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all observables in the bundle (e.g. observables that might result from predefined rule).

+

You can also add all indicators and relationships generated by this component in the entity having triggered the playbook, if this entity is a container.

+

Extract observables from indicator

+

Will extract observables based on indicators contained in the received STIX 2.1 bundle.

+

By default, it is applied to entities having triggered the playbook. You can toggle the corresponding option to apply it to all indicators in the bundle (e.g. indicators that might result from enrichment.

+

You can also add all observables and relationships generated by this component in the entity having triggered the playbook, if this entity is a container.

+

Reduce Knowledge

+

Will elagate the received STIX 2.1 bundle based on the configured filter.

+

Monitor playbook activity

+

At the top right of the interface, you can access execution trace of your playbook and consult the raw data after every step of your playbook execution.

+

Steps monitoring

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/background-tasks/index.html b/6.2.X/usage/background-tasks/index.html new file mode 100755 index 00000000..4cc78943 --- /dev/null +++ b/6.2.X/usage/background-tasks/index.html @@ -0,0 +1,5072 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Background tasks - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Background tasks

+

Three types of tasks are done in the background:

+
    +
  • rule tasks,
  • +
  • knowledge tasks,
  • +
  • user tasks.
  • +
+

Rule tasks can be seen and activated in Settings > Customization > Rules engine. +Knowledge and user tasks can be seen and managed in Data > Background Tasks. The scope of each task is indicated.

+

Background_tasks

+

Rule tasks

+

If a rule task is enabled, it leads to the scan of the whole platform data and the creation of entities or relationships in case a configuration corresponds to the tasks rules. The created data are called 'inferred data'. Each time an event occurs in the platform, the rule engine checks if inferred data should be updated/created/deleted.

+

Knowledge tasks

+

Knowledge tasks are background tasks updating or deleting entities and correspond to mass operations on these data. To create one, select entities via the checkboxes in an entity list, and choose the action to perform via the toolbar.

+

Rights

+
    +
  • To create a knowledge task, the user should have the capability to Update Knowledge (or the capability to delete knowledge if the task action is a deletion).
  • +
  • To see a knowledge task in the Background task section, the user should be the creator of the task, or have the KNOWLEDGE capability.
  • +
  • To delete a knowledge task from the Background task section, the user should be the creator of the task, or have the KNOWLEDGE_UPDATE capability.
  • +
+

User tasks

+

User tasks are background tasks updating or deleting notifications. It can be done from the Notification section, by selecting several notifications via the checkboxes, and choosing an action via the toolbar.

+

Rights

+
    +
  • A user can create a user task on its own notifications only.
  • +
  • To see or delete a user task, the user should be the creator of the task or have the SET_ACCESS capability.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/case-management/index.html b/6.2.X/usage/case-management/index.html new file mode 100755 index 00000000..8984a0c9 --- /dev/null +++ b/6.2.X/usage/case-management/index.html @@ -0,0 +1,5014 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Case management - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Case management

+

Why Case management?

+

Compiling CTI data in one place, deduplicate and correlate to transform it into Intelligence is very important. But ultimately, you need to act based on this Intelligence. Some situations will need to be taken care of, like cybersecurity incidents, requests for information or requests for takedown. Some actions will then need to be traced, to be coordinated and oversaw. Some actions will include feedback and content delivery.

+

OpenCTI includes Cases to allow organizations to manage situations and organize their team's work. Better, by doing Case management in OpenCTI, you handle your cases with all the context and Intelligence you need, at hand.

+

How to manage your Case in OpenCTI?

+

Multiple situations can be modelize in OpenCTI as a Case, either an Incident Response, a Request for Takedown or a Request for Information.

+

Incident Responses' list

+

All Cases can contain any entities and relationships you need to represent the Intelligence context related to the situation. At the beginning of your case, you may find yourself with only some Observables sighted in a system. At the end, you may have Indicators, Threat Actor, impacted systems, attack patterns. All representing your findings, ready to be presented and exported as graph, pdf report, timeline, etc.

+

Incident Responses' list

+

Some Cases may need some collaborative work and specific Tasks to be performed by people that have relevant skillsets. OpenCTI allows you to associate Tasks in your Cases and assign them to users in the platform. As some type of situation may need the same tasks to be done, it is also possible to pre-define lists of tasks to be applied on your case. You can define these lists by accessing the Settings/Taxonomies/Case templates panel. Then you just need to add it from the overview of your desire Case.

+

Tip: A user can have a custom dashboard showing him all the tasks that have been assigned to him.

+

Incident Responses' list

+

As with other objects in OpenCTI, you can also leverage the Notes to add some investigation and analysis related comments, helping you shaping up the content of your case with unstructured data and trace all the work that have been done.

+

You can also use Opinions to collect how the Case has been handled, helping you to build Lessons Learned.

+

Incident Responses' list

+

To trace the evolution of your Case and define specific resolution worflows, you can use the Status (that can be define in Settings/Taxonomies/Status templates).

+

At the end of your Case, you will certainly want to report on what has been done. OpenCTI allows you to export the content of the Case in a simple but customizable PDF (currently in refactor). But of course, your company has its own documents' templates, right? With OpenCTI, you will be able to include some nice graphics in it. For example, a Matrix view of the attacker attack pattern or even a graph display of how things are connected.

+

Also, we are currently working a more meaningfull Timeline view that will be possible to export too.

+

Use case example: A suspicious observable is sighted by a defense system. Is it important?

+
    +
  • Daily, your SIEM and EDR are fed Indicators of Compromise from your OpenCTI instance.
  • +
  • Today, your SIEM has sighted the domain name "bad.com" matching one of them. Its alert has been transfered to OpenCTI and has created a Sighting relationship between your System "SIEM permiter A" and the Observable "bad.com".
  • +
  • You are alerted immediatly, because you have activated the inference rule creating a corresponding Incident in this situation, and you have created an alert based on new Incident that sends you email notification and Teams message (webhook).
  • +
  • In OpenCTI, you can clearly see the link between the alerting System, the sighted Observable and the corresponding Indicator. Better, you can also see all the context of the Indicator. It is linked to a notorious and recent phishing campaign targeting your activity sector. "bad.com" is clearly something to investigate ASAP.
  • +
  • You quickly select all the context you have found, and add it to a new Incident responsecase. You position the priority to High, regarding the context, and the severity to Low, as you don't know yet if someone really interacted with "bad.com".
  • +
  • You also assign the case to one of your colleagues, on duty for investigative work. To guide him, you also create a Task in your case for verifying if an actual interaction happened with "bad.com".
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/containers/index.html b/6.2.X/usage/containers/index.html new file mode 100755 index 00000000..b4484825 --- /dev/null +++ b/6.2.X/usage/containers/index.html @@ -0,0 +1,5178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Containers - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Containers

+

STIX standard

+

Definition

+

In the STIX 2.1 standard, some STIX Domain Objects (SDO) can be considered as "container of knowledge", using the object_refs attribute to refer multiple other objects as nested references. In object_refs, it is possible to refer to entities and relationships.

+

Example

+
{
+   "type": "report",
+   "spec_version": "2.1",
+   "id": "report--84e4d88f-44ea-4bcd-bbf3-b2c1c320bcb3",
+   "created_by_ref": "identity--a463ffb3-1bd9-4d94-b02d-74e4f1658283",
+   "created": "2015-12-21T19:59:11.000Z",
+   "modified": "2015-12-21T19:59:11.000Z",
+   "name": "The Black Vine Cyberespionage Group",
+   "description": "A simple report with an indicator and campaign",
+   "published": "2016-01-20T17:00:00.000Z",
+   "report_types": ["campaign"],
+   "object_refs": [
+      "indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
+      "campaign--83422c77-904c-4dc1-aff5-5c38f3a2c55c",
+      "relationship--f82356ae-fe6c-437c-9c24-6b64314ae68a"
+   ]
+}
+
+

In the previous example, we have a nested reference to 3 other objects:

+
"object_refs": [
+   "indicator--26ffb872-1dd9-446e-b6f5-d58527e5b5d2",
+   "campaign--83422c77-904c-4dc1-aff5-5c38f3a2c55c",
+   "relationship--f82356ae-fe6c-437c-9c24-6b64314ae68a"
+]
+
+

Implementation

+

Types of container

+

In OpenCTI, containers are displayed differently than other entities, because they contain pieces of knowledge. Here is the list of containers in the platform:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type of entitySTIX standardDescription
ReportNativeReports are collections of threat intelligence focused on one or more topics, such as a description of a threat actor, malware, or attack technique, including context and related details.
GroupingNativeA Grouping object explicitly asserts that the referenced STIX Objects have a shared context, unlike a STIX Bundle (which explicitly conveys no context).
Observed DataNativeObserved Data conveys information about cyber security related entities such as files, systems, and networks using the STIX Cyber-observable Objects (SCOs).
NoteNativeA Note is intended to convey informative text to provide further context and/or to provide additional analysis not contained in the STIX Objects.
OpinionNativeAn Opinion is an assessment of the correctness of the information in a STIX Object produced by a different entity.
CaseExtensionA case whether an Incident Response, a Request for Information or a Request for Takedown is used to convey an epic with a set of tasks.
TaskExtensionA task, generally used in the context of a case, is intended to convey information about something that must be done in a limited timeframe.
+

Containers behavior

+

In the platform, it is always possible to visualize the list of entities and/or observables referenced in a container (Container > Entities or Observables) but also to add / remove entities from the container.

+

Entities

+

As containers can also contain relationships, which are generally linked to the other entities in the container, it is also possible to visualize the container as a graph (Container > Knowledge)

+

Graph

+

Containers of an entity or a relationship

+

On the entity or the relationship side, you can always find all containers where the object is contained using the top menu Analysis:

+

Analysis

+

In all containers list, you can also filter containers based on one or multiple contained object(s):

+

Container filters

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/dashboards-share/index.html b/6.2.X/usage/dashboards-share/index.html new file mode 100755 index 00000000..959b531b --- /dev/null +++ b/6.2.X/usage/dashboards-share/index.html @@ -0,0 +1,5064 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Share custom dashboards - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Share custom dashboards

+

OpenCTI provides a simple way to share a visualisation of a custom dashboard to anyone, +even for people that are outside of the platform. We call those visualisations: public dashboards.

+

Public dashboards are a snapshot of a custom dashboard at a specific moment of time. +By this way you can share a version of a custom dashboard, then modify your custom dashboard +without worrying about the impact on public dashboards you have created.

+

On the contrary, if you want that your public dashboard is updated with the last version of the +associated custom dashboard, you can do it with few clicks by recreating a public dashboard using +the same name of the one to update.

+
+

To be able to share custom dashboards you need to have the Manage data sharing & ingestion capability.

+
+

Create a public dashboard

+

On the top-right of your custom dashboard page you will find a button that will open a panel to +manage the public dashboards associated to this custom dashboard.

+

Share dashboard button

+

In this panel you will find two parts: +- At the top you have a form allowing you to create public dashboards, +- And below, the list of the public dashboards you have created.

+

Share dashboard panel

+

Form to create a new public dashboard

+

First you need to specify a name for your public dashboard. This name will be displayed +on the dashboard page. The name is also used to generate an ID for your public dashboard +that will be used in the URL to access the dashboard.

+

The ID is generated as follow: replace all spaces with symbol - and remove special characters. +This ID also needs to be unique in the platform as it is used in the URL to access the dashboard.

+

Share dashboard name

+

Then you can choose if the public dashboard is enabled or not. A disabled dashboard means that +you cannot access the public dashboard through the custom URL. But you can still manage it from this +panel.

+

Share dashboard enable

+

Finally you choose the max level of marking definitions for the data to be displayed in the +public dashboard. For example if you choose TLP:AMBER then the data fetched by the widgets +inside the public dashboard will be at maximum AMBER, you won't retrieved data with RED +marking definition.

+

Also note that the list of marking definitions you can see is based on your current marking +access on the platform and the maximum sharable marking definitions defined in your groups.

+
+

Define maximum shareable markings in groups

+

As a platform administrator, you can define, for each group and for each type of marking definition, Maximum marking definitions shareable to be +shared through Public Dashboard, regardless of the definition set by users in their public dashboard.

+
+

Share dashboard markings

+

List of the public dashboards

+

When you have created a public dashboard, it will appear in the list below the form.

+

Share dashboard list

+

In this list each item represents a public dashboard you have created. For each you can see its +name, path of the URL, max marking definitions, the date of creation, the status to know if +the dashboard is enabled or not and some actions.

+

The possible actions are: copy the link of the public dashboard, disable or enable the dashboard +and delete the dashboard.

+

To share a public dashboard just copy the link and give the URL to the person you want to share +with. The dashboard will be visible even if the person is not connected to OpenCTI.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/dashboards/index.html b/6.2.X/usage/dashboards/index.html new file mode 100755 index 00000000..76158697 --- /dev/null +++ b/6.2.X/usage/dashboards/index.html @@ -0,0 +1,5214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Custom dashboards - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Custom dashboards

+

OpenCTI provides an adaptable and entirely customizable dashboard functionality. The flexibility of OpenCTI's dashboard ensures a tailored and insightful visualization of data, fostering a comprehensive understanding of the platform's knowledge, relationships, and activities.

+

Dashboard overview

+

You have the flexibility to tailor the arrangement of widgets on your dashboard, optimizing organization and workflow efficiency. Widgets can be intuitively placed to highlight key information. Additionally, you can resize widgets from the bottom right corner based on the importance of the information, enabling adaptation to specific analytical needs. This technical flexibility ensures a fluid, visually optimized user experience.

+

Moreover, the top banner of the dashboard offers a convenient feature to configure the timeframe for displayed data. This can be accomplished through the selection of a relative time period, such as "Last 7 days", or by specifying fixed "Start" and "End" dates, allowing users to precisely control the temporal scope of the displayed information.

+

Dashboard overview

+

+

Access control

+

In OpenCTI, the power to create custom dashboards comes with a flexible access control system, allowing users to tailor visibility and rights according to their collaborative needs.

+

When a user crafts a personalized dashboard, by default, it remains visible only to the dashboard creator. At this stage, the creator holds administrative access rights. However, they can extend access and rights to others using the "Manage access" button, denoted by a locker icon, located at the top right of the dashboard page.

+

Manage access button

+

Levels of access:

+
    +
  • View: Access to view the dashboard.
  • +
  • Edit: View + Permission to modify and update the dashboard and its widgets.
  • +
  • Manage: Edit + Ability to delete the dashboard and to control user access and rights.
  • +
+

It's crucial to emphasize that at least one user must retain admin access to ensure ongoing management capabilities for the dashboard.

+

Manage access dialog

+
+

Knowledge access restriction

+

The platform's data access restrictions also apply to dashboards. The data displayed in the widgets is subject to the user's access rights within the platform. Therefore, an admin user will not see the same results in the widgets as a user with limited access, such as viewing only TLP:CLEAR data (assuming the platform contains data beyond TLP:CLEAR).

+
+

Share dashboard and widget configurations

+

OpenCTI provides functionality for exporting, importing and duplicating dashboard and widget configurations, facilitating the seamless transfer of customized dashboard setups between instances or users.

+

Export

+

Dashboards can be exported from either the custom dashboards list or the dashboard view.

+

To export a dashboard configuration from the custom dashboards list:

+
    +
  1. Click on the burger menu button at the end of the dashboard line.
  2. +
  3. Select Export.
  4. +
+

Export dashboard option

+

To export a widget, the same mechanism is used, but from the burger menu button in the upper right-hand corner of the widget.

+

Export widget option

+

To export a dashboard configuration from the dashboard view:

+
    +
  1. Navigate to the desired dashboard.
  2. +
  3. Click on the Export button (file with an arrow pointing to the top right corner) located in the top-right corner of the dashboard.
  4. +
+

Export JSON button

+

Configuration file

+

The dashboard configuration will be saved as a JSON file, with the title formatted as [YYYYMMDD]_octi_dashboard_[dashboard title]. The expected configuration file content is as follows:

+
{
+  "openCTI_version": "5.12.0",
+  "type": "dashboard",
+  "configuration": {
+    "name": "My dashboard title",
+    "manifest": "eyJ3aWRn(...)uZmlnIjp7fX0="
+  }
+}
+
+

The widget configuration will be saved as a JSON file, with the title formatted as [YYYYMMDD]_octi_widget_[widget view]. The expected configuration file content is as follows:

+
{
+  "openCTI_version": "5.12.0",
+  "type": "widget",
+  "configuration": "eyJ0eXB(...)iOmZhbHNlfX0="
+}
+
+
+

Source platform-related filters

+

When exporting a dashboard or widget configuration, all filters will be exported as is. Filters on objects that do not exist in the receiving platform will need manual deletion after import. Filters to be deleted can be identified by their "delete" barred value.

+
+

Filter to delete

+

Import

+

Dashboards can be imported from the custom dashboards list:

+
    +
  1. Hover over the Add button (+) in the right bottom corner.
  2. +
  3. Click on the Import dashboard button (cloud with an upward arrow).
  4. +
  5. Select your file.
  6. +
+

Import dashboard option

+

To import a widget, the same mechanism is used, but from a dashboard view.

+

Import widget option

+
+

Configuration compatibility

+

Only JSON files with the required properties will be accepted, including "openCTI_version: [5.12.0 and above]", "type: [dashboard|widget]", and a "configuration". This applies to both dashboards and widgets configurations.

+
+

Duplicate

+

Dashboards can be duplicated from either the custom dashboards list or the dashboard view.

+

To duplicate a dashboard from the custom dashboards list:

+
    +
  1. Click on the burger menu button at the end of the dashboard line.
  2. +
  3. Select Duplicate.
  4. +
+

To duplicate a widget, the same mechanism is used, but from the burger menu button in the upper right-hand corner of the widget.

+

To duplicate a dashboard from the dashboard view:

+
    +
  1. Navigate to the desired dashboard.
  2. +
  3. Click on the Duplicate the dashboard button (two stacked sheets) located in the top-right corner of the dashboard.
  4. +
+

Duplicate dashboard button

+

Upon successful duplication, a confirmation message is displayed for a short duration, accompanied by a link for easy access to the new dashboard view. Nevertheless, the new dashboard can still be found in the dashboards list.

+

duplicate-dashboard-success-message

+
+

Dashboard access

+

The user importing or duplicating a dashboard becomes the only one with access to it. Then, access can be managed as usual.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/data-model/index.html b/6.2.X/usage/data-model/index.html new file mode 100755 index 00000000..1fb3578c --- /dev/null +++ b/6.2.X/usage/data-model/index.html @@ -0,0 +1,5126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data model - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Data model

+

Introduction

+

The OpenCTI core design relies on the concept of a knowledge graph, where you have two different kinds of object:

+
    +
  1. Nodes are used to describe entities, which have some properties or attributes.
  2. +
  3. Edges are used to describe relationships, which are created between two entity nodes and have some properties or attributes.
  4. +
+
+

Example

+

An example would be that the entity APT28 has a relationship uses to the malware entity Drovorub.

+
+

Standard

+

+

The STIX model

+

To enable a unified approach in the description of threat intelligence knowledge as well as importing and exporting data, the OpenCTI data model is based on the STIX 2.1 standard. Thus we highly recommend to take a look to the STIX Introductory Walkthrough and to the different kinds of STIX relationships to get a better understanding of how OpenCTI works.

+

Some more important STIX naming shortcuts are:

+
    +
  • STIX Domain Objects (SDO): Attack Patterns, Malware, Threat Actors, etc.
  • +
  • STIX Cyber Observable (SCO): IP Addresses, domain names, hashes, etc.
  • +
  • STIX Relationship Object (SRO): Relationships, Sightings
  • +
+

STIX meta model

+

Extensions

+

In some cases, the model has been extended to be able to:

+
    +
  • Support more types of SCOs to modelize information systems such as cryptocurrency wallets, user agents, etc.
  • +
  • Support more types of SDOs to modelize disinformation and cybercrime such as channels, events, narrative, etc.
  • +
  • Support more types of SROs to extend the new SDOs such asamplifies, publishes, etc.
  • +
+

Implementation in the platform

+

Diagram of types

+

You can find below the digram of all types of entities and relationships available in OpenCTI.

+ + +

Attributes and properties

+

To get a comprehensive list of available properties for a given type of entity or relationship, you can use the GraphQL playground schema available in your "Profile > Playground". Then you can click on schema. You can for instance search for the keyword IntrusionSet:

+

STIX meta model

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/dates/index.html b/6.2.X/usage/dates/index.html new file mode 100755 index 00000000..893238d5 --- /dev/null +++ b/6.2.X/usage/dates/index.html @@ -0,0 +1,5035 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Meaning of dates - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Meaning of dates

+

In OpenCTI, entities can contain various dates, each representing different types of information. The available dates vary depending on the entity types.

+

Dates

+

In OpenCTI, dates play a crucial role in understanding the context and history of entities. Here's a breakdown of the different dates you might encounter in the platform:

+
    +
  • “Platform creation date”: This date signifies the moment the entity was created within OpenCTI. On the API side, this timestamp corresponds to the created_at field. It reflects the initiation of the entity within the OpenCTI environment.
  • +
  • “Original creation date”: This date reflects the original creation date of the data on the source's side. It becomes relevant if the source provides this information and if the connector responsible for importing the data takes it into account. In cases where the source date is unavailable or not considered, this date defaults to the import date (i.e. the “Platform creation date”). On the API side, this timestamp corresponds to the created field.
  • +
  • “Modification date”: This date captures the most recent modification made to the entity, whether a connector automatically modifies it or a user manually edits the entity. On the API side, this timestamp corresponds to the updated_at field. It serves as a reference point for tracking the latest changes made to the entity.
  • +
  • Date not shown on GUI: There is an additional date which is not visible on the entity in the GUI. This date is the modified field on the API. This date reflects the original update date of the data on the source's side. The difference between modified and updated_at is identical to the difference between created and created_at.
  • +
+

Dates

+

Understanding these dates is pivotal for contextualizing the information within OpenCTI, ensuring a comprehensive view of entity history and evolution.

+

Date types

+

Technical dates

+

The technical dates refer to the dates directly associated to data management within the platform. The API fields corresponding to technical dates are:

+
    +
  • created_at: Indicates the date and time when the entity was created in the platform.
  • +
  • updated_at: Represents the date and time of the most recent update to the entity in the platform.
  • +
+

Functional dates

+

The functional dates are the dates functionally significant, often indicating specific events or milestones. The API fields corresponding to functional dates are:

+
    +
  • created: Denotes the date and time when the entity was created on the source's side.
  • +
  • modified: Represents the date and time of the most recent modification to the entity on the source's side.
  • +
  • start_time: Indicates the start date and time associated with a relationship.
  • +
  • stop_time: Indicates the stop date and time associated with a relationship.
  • +
  • first_seen: Represents the initial date and time when the entity/activity was observed.
  • +
  • last_seen: Represents the most recent date and time when the entity/activity was observed.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/deduplication/index.html b/6.2.X/usage/deduplication/index.html new file mode 100755 index 00000000..2cc83f89 --- /dev/null +++ b/6.2.X/usage/deduplication/index.html @@ -0,0 +1,5229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Deduplication - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Deduplication

+

One of the core concept of the OpenCTI knowledge graph is all underlying mechanisms implemented to accurately de-duplicate and consolidate (aka. upserting) information about entities and relationships.

+

Creation behavior

+

When an object is created in the platform, whether manually by a user or automatically by the connectors / workers chain, the platform checks if something already exist based on some properties of the object. If the object already exists, it will return the existing object and, in some cases, update it as well.

+

Technically, OpenCTI generates deterministic IDs based on the listed properties below to prevent duplicate (aka "ID Contributing Properties"). Also, it is important to note that there is a special link between name and aliases leading to not have entities with overlapping aliases or an alias already used in the name of another entity.

+

Entities

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeAttributes
Area(name OR x_opencti_alias) AND x_opencti_location_type
Attack Pattern(name OR alias) AND optional x_mitre_id
Campaignname OR alias
Channelname OR alias
City(name OR x_opencti_alias) AND x_opencti_location_type
Country(name OR x_opencti_alias) AND x_opencti_location_type
Course Of Action(name OR alias) AND optional x_mitre_id
Data Componentname OR alias
Data Sourcename OR alias
Eventname OR alias
Feedback Casename AND created (date)
Groupingname AND context
Incidentname OR alias
Incident Response Casename OR alias
Indicatorpattern OR alias
Individual(name OR x_opencti_alias) and identity_class
Infrastructurename OR alias
Intrusion Setname OR alias
Languagename OR alias
Malwarename OR alias
Malware Analysisname OR alias
Narrativename OR alias
NoteNone
Observed Dataname OR alias
OpinionNone
Organization(name OR x_opencti_alias) and identity_class
Position(name OR x_opencti_alias) AND x_opencti_location_type
Regionname OR alias
Reportname AND published (date)
RFI Casename AND created (date)
RFT Casename AND created (date)
Sector(name OR alias) and identity_class
TaskNone
Threat Actorname OR alias
Toolname OR alias
Vulnerabilityname OR alias
+
+

Names and aliases management

+

The name and aliases of an entity define a set of unique values, so it's not possible to have the name equal to an alias and vice versa.

+
+

Relationships

+

The deduplication process of relationships is based on the following criterias:

+
    +
  • Type
  • +
  • Source
  • +
  • Target
  • +
  • Start time between -30 days / + 30 days
  • +
  • Stop time between -30 days / + 30 days
  • +
+

Observables

+

For STIX Cyber Observables, OpenCTI also generate deterministic IDs based on the STIX specification using the "ID Contributing Properties" defined for each type of observable.

+

Update behavior

+

In cases where an entity already exists in the platform, incoming creations can trigger updates to the existing entity's attributes. +This logic has been implemented to converge the knowledge base towards the highest confidence and quality levels for both entities and relationships.

+

To understand in details how the deduplication mechanism works in context of the maximum confidence level, you can navigate through this diagram (section deduplication):

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/delete-restore/index.html b/6.2.X/usage/delete-restore/index.html new file mode 100755 index 00000000..1f995541 --- /dev/null +++ b/6.2.X/usage/delete-restore/index.html @@ -0,0 +1,5038 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Delete and restore knowledge - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Delete and restore knowledge

+

Knowledge can be deleted from OpenCTI either in an overview of an object or using background tasks. +When an object is deleted, all its relationships and references to other objects are also deleted.

+

The deletion event is written to the stream, to trigger automated playbooks or synchronize another platform.

+

Since OpenCTI 6.1, a record of the deleted objects is kept for a given period of a time, allowing to restore them on demand. This does not impact the stream events or other side effect of the deletion: the object is still deleted.

+

Trash

+

A view called "Trash" displays all "delete" operations, entities and relationships alike.

+

Trash

+

A delete operation contains not only the entity or relationship that has been deleted, but also all the relationships and references from (to) this main object to (from) other elements in the platform.

+

You can sort, filter or search this table using the usual UI controls. You are limited to the type of object, their representation (most of the time, the name of the object), the user who deleted the object, the date and time of deletion and the marking of the object.

+

Note that the delete operations (i.e. the entries in this table view) inherit the marking of the main entity that was deleted, and thus follow the same access restriction as the object that was deleted.

+

You can individually restore or permanently delete an object from the trash view using the burger menu at the end of the line.

+

Trash actions

+

Alternatively, you can use the checkboxes at the start of the line to select a subset of deleted objects, and trigger a background task to restore or permanently delete them by batch.

+

Restore

+

Restoring an element creates it again in the platform with the same information it had before its deletion. +It also restores all the relationships from or to this object, that have been also deleted during the deletion operation. +If the object had attached files (uploaded or exported), they are also restored.

+

Trash restore confirm

+

Permanent delete

+

From the Trash panel, it is also possible to delete permanently the object, its relationships, and attached files.

+

Trash delete confirm

+

Trash retention

+

Deleted objects are kept in trash during a fixed period of time (7 days by default), then they are permanently deleted by the trash manager.

+

Limitations

+

When it comes to restoring a deleted object from the trash, the current implementation shows several limitations. +First and foremost, if an object in the trash has lost a relationship dependency (i.e. the other side of a relationship from or to this object is no longer in live database), you will not be able to restore the object.

+

restore error: a dependency is in the trash

+

In such case, if the missing dependency is in the trash too, you can manually restore this dependency first and then retry.

+

If the missing dependency has been permanently deleted, the object cannot be recovered.

+

restore error: a dependency is in the trash

+

In other words: +* no partial restore: the object and all its relationships must be restored in one pass +* no "cascading" restore: restoring one object does not restore automatically all linked objects in the trash

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/enrichment/index.html b/6.2.X/usage/enrichment/index.html new file mode 100755 index 00000000..d83944e6 --- /dev/null +++ b/6.2.X/usage/enrichment/index.html @@ -0,0 +1,4979 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enrichment connectors - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Enrichment connectors

+

Enriching the data within the OpenCTI platform is made seamlessly through the integration of enrichment connectors. These connectors facilitate the retrieval of additional data from external sources or portals.

+

Automatic enrichment

+

Enrichment can be conducted automatically in two distinct modes:

+
    +
  • Upon data arrival: Configuring the connector to run automatically when data arrives in OpenCTI ensures a real-time enrichment process, supplementing the platform's data. However, it's advisable to avoid automatic enrichment for quota-based connectors to paid sources to prevent quickly depleting all quotas. Additionally, this automatic enrichment contributes to increased data volume. On a large scale, with hundreds of thousands of objects, the disk space occupied by this data can be substantial, and it should be considered, especially if disk space is a concern. The automatic execution is configured at the connector level using the "auto: true|false" parameter.
  • +
  • Targeted enrichment via playbooks: Enrichment can also be performed in a more targeted manner using playbooks. This approach allows for a customized enrichment strategy, focusing on specific objects and optimizing the relevance of the retrieved data.
  • +
+

Manual enrichment

+

Manually initiating the enrichment process is straightforward. Simply locate the button with the cloud icon at the top right of an entity.

+

Enrichment button

+

Clicking on this icon unveils a side panel displaying a list of available connectors that can be activated for the given object. If no connectors appear in the panel, it indicates that no enrichment connectors are available for the specific type of object in focus.

+

Enrichment panel

+

Activation of an enrichment connector triggers a contact with the designated remote source, importing a set of data into OpenCTI to enrich the selected object. Each enrichment connector operates uniquely, focusing on a specific set of object types it can enrich and a distinct set of data it imports. Depending on the connectors, they may, establish relationships, add external references, or complete object information, thereby contributing to the comprehensiveness of information within the platform.

+

The list of available connectors can be found in our connectors catalog. In addition, further documentation on connectors is available on the dedicated documentation page.

+
+

Impact of the max confidence level

+
+

The maximum confidence level per user can have an impact on enrichment connectors, not being able to update data in the platform. To understand the concept and the potential issues you could face, please navigate to this page to understand.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-analysis/index.html b/6.2.X/usage/exploring-analysis/index.html new file mode 100755 index 00000000..47bc4158 --- /dev/null +++ b/6.2.X/usage/exploring-analysis/index.html @@ -0,0 +1,5317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Analysis - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Analyses

+

When you click on "Analyses" in the left-side bar, you see all the "Analyses" tabs, visible on the top bar on the left. By default, the user directly access the "Reports" tab, but can navigate to the other tabs as well.

+

From the Analyses section, users can access the following tabs:

+
    +
  • Reports: See Reports as a sort of containers to detail and structure what is contained on a specific report, either from a source or write by yourself. Think of it as an Intelligence Production in OpenCTI.
  • +
  • Groupings: Groupings are containers, like Reports, but do not represent an Intelligence Production. They regroup Objects sharing an explicit context. For example, a Grouping might represent a set of data that, in time, given sufficient analysis, would mature to convey an incident or threat report as Report container.
  • +
  • Malware Analyses: As define by STIX 2.1 standard, Malware Analyses captures the metadata and results of a particular static or dynamic analysis performed on a malware instance or family.
  • +
  • Notes: Through this tab, you can find all the Notes that have been written in the platform, for example to add some analyst's unstructured knowledge about an Object.
  • +
  • External references: Intelligence is never created from nothing. External references give user a way to link sources or reference documents to any Object in the platform.
  • +
+

Analyses Default page is Reports

+

Reports

+

General presentation

+

Reports are one of the central component of the platform. It is from a Report that knowledge is extracted and integrated in the platform for further navigation, analyses and exports. Always tying the information back to a report allows for the user to be able to identify the source of any piece of information in the platform at all time.

+

In the MITRE STIX 2.1 documentation, a Report is defined as such :

+
+

Reports are collections of threat intelligence focused on one or more topics, such as a description of a threat actor, malware, or attack technique, including context and related details. They are used to group related threat intelligence together so that it can be published as a comprehensive cyber threat story.

+
+

As a result, a Report object in OpenCTI is a set of attributes and metadata defining and describing a document outside the platform, which can be a threat intelligence report from a security reseearch team, a blog post, a press article a video, a conference extract, a MISP event, or any type of document and source.

+

When clicking on the Reports tab at the top left, you see the list of all the Reports you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of reports.

+

Visualizing Knowledge within a Report

+

When clicking on a Report, you land on the Overview tab. For a Report, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge contained in the report, accessible through different views (See below for a dive-in). As described here.
  • +
  • Content: a tab to upload or creates outcomes document displaying the content of the Report (for example in PDF, text, HTML or markdown files). The Content of the document is displayed to ease the access of Knowledge through a readable format. As described here.
  • +
  • Entities: A table containing all SDO (Stix Domain Objects) contained in the Report, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • +
  • Observables: A table containing all SCO (Stix Cyber Observable) contained in the Report, with search and filters available. It also displays if the SCO has been added directly or through inferences with the reasoning engine
  • +
  • Data: as described here.
  • +
+

Exploring and modifying the structured Knowledge contained in a Report can be done through different lenses.

+

Graph View

+

Graph View of a Report

+

In Graph view, STIX SDO are displayed as graph nodes and relationships as graph links. Nodes are colored depending of their type. Direct relationship are displayed as plain link and inferred relationships in dotted link. +At the top right, you will find a serie of icons. From there you can change the current type of view. Here you can also perform global action on the Knowledge of the Report. Let's highlight 2 of them: +- Suggestions: This tool suggests you some logical relationships to add between your contained Object to give more consistency to your Knowledge. +- Share with an Organization: if you have designated a main Organization in the platform settings, you can here share your Report and its content with users of an other Organization. +At the bottom, you have many option to manipulate the graph: +- Multiple option for shaping the graph and applying forces to the nodes and links +- Multiple selection options +- Multiple filters, including a time range selector allowing you to see the evolution of the Knowledge within the Report. +- Multiple creation and edition tools to modify the Knowledge contained in the Report.

+

Content mapping view

+

Content mapping view of a Report

+

Through this view, you can map exsisting or new Objects directly from a readable content, allowing you to quickly append structured Knowledge in your Report before refining it with relationships and details. +This view is a great place to see the continuum between unstructured and structured Knowledge of a specific Intelligence Production.

+

Timeline view

+

Timeline view of a Report

+

This view allows you to see the structured Knowledge chronologically. This view is really useful when the report describes an attack or a campaign that lasted some time, and the analyst payed attention to the dates. +The view can be filtered and displayed relationships too.

+

Correlation view

+

Correlation view of a Report

+

The correlation view is a great way to visualize and find other Reports related to your current subject of interest. This graph displays all Report related to the important nodes contained in your current Report, for example Objects like Malware or Intrusion sets.

+

Matrix view

+

Matrix view of a Report

+

If your Report describes let's say an attack, a campaign, or an understanding of an Intrusion set, it should contains multiple attack patterns Objects to structure the Knowledge about the TTPs of the Threat Actor. Those attack patterns can be displayed as highlighted matrices, by default the MITRE ATT&CK Enterprise matrix. As some matrices can be huge, it can be also filtered to only display attack patterns describes in the Report.

+

Groupings

+

Groupings are an alternative to Report for grouping Objects sharing a context without describing an Intelligence Production.

+

In the MITRE STIX 2.1 documentation, a Grouping is defined as such :

+
+

A Grouping object explicitly asserts that the referenced STIX Objects have a shared context, unlike a STIX Bundle (which explicitly conveys no context). A Grouping object should not be confused with an intelligence product, which should be conveyed via a STIX Report. A STIX Grouping object might represent a set of data that, in time, given sufficient analysis, would mature to convey an incident or threat report as a STIX Report object. For example, a Grouping could be used to characterize an ongoing investigation into a security event or incident. A Grouping object could also be used to assert that the referenced STIX Objects are related to an ongoing analysis process, such as when a threat analyst is collaborating with others in their trust community to examine a series of Campaigns and Indicators.

+
+

When clicking on the Groupings tab at the top of the interface, you see the list of all the Groupings you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the groupings.

+

Clicking on a Grouping, you land on its Overview tab. For a Groupings, the following tabs are accessible: +- Overview: as described here. +- Knowledge: a complex tab that regroups all the structured Knowledge contained in the groupings, as for a Report, except for the Timeline view. As described here. +- Entities: A table containing all SDO (Stix Domain Objects) contained in the Grouping, with search and filters available. It also display if the SDO has been added directly or through inferences with the reasonging engine +- Observables: A table containing all SCO (Stix Cyber Observable) contained in the Grouping, with search and filters available. It also display if the SDO has been added directly or through inferences with the reasonging engine +- Data: as described here.

+

Malware Analyses

+

Malware analyses are an important part of the Cyber Threat Intelligence, allowing an precise understanding of what and how a malware really do on the host but also how and from where it receives its command and communicates its results.

+

In OpenCTI, Malware Analyses can be created from enrichment connectors that will take an Observable as input and perform a scan on a online service platform to bring back results. As such, Malware Analyses can be done on File, Domain and URL.

+

In the MITRE STIX 2.1 documentation, a Malware Analyses is defined as such :

+
+

Malware Analyses captures the metadata and results of a particular static or dynamic analysis performed on a malware instance or family.

+
+

When clicking on the Malware Analyses tab at the top of the interface, you see the list of all the Malware Analyses you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the Malware Analyses.

+

Clicking on a Malware Analyses, you land on its Overview tab. The following tabs are accessible: +- Overview: This view contains some additions from the common Overview here. You will find here details about how the analysis have been performed, what is the global result regarding the malicioussness of the analysed artifact and all the Observables that have been found during the analysis. +- Knowledge: If you Malware analysis is linked to other Objects that are not part of the analysis result, they will be displayed here. As described here. +- Data: as described here. +- History: as described here.

+

Malware Analyses Overview

+

Notes

+

Not every Knowledge can be structured. For allowing any users to share their insights about a specific Knowledge, they can create a Note for every Object and relationship in OpenCTI they can access to. All the Notes are listed within the Analyses menu for allowing global review of this unstructured addition to the global Knowledge.

+

In the MITRE STIX 2.1 documentation, a Note is defined as such :

+
+

A Note is intended to convey informative text to provide further context and/or to provide additional analysis not contained in the STIX Objects, Marking Definition objects, or Language Content objects which the Note relates to. Notes can be created by anyone (not just the original object creator).

+
+

Clicking on a Note, you land on its Overview tab. The following tabs are accessible: +- Overview: as described here. +- Data: as described here. +- History: as described here.

+

External references

+

Intelligence is never created from nothing. External references give user a way to link sources or reference documents to any Object in the platform. All external references are listed within the Analyses menu for accessing directly sources of the structured Knowledge.

+

In the MITRE STIX 2.1 documentation, a External references is defined as such :

+
+

External references are used to describe pointers to information represented outside of STIX. For example, a Malware object could use an external reference to indicate an ID for that malware in an external database or a report could use references to represent source material.

+
+

Clicking on an External reference, you land on its Overview tab. The following tabs are accessible: +- Overview: as described here.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-arsenal/index.html b/6.2.X/usage/exploring-arsenal/index.html new file mode 100755 index 00000000..17bdaadd --- /dev/null +++ b/6.2.X/usage/exploring-arsenal/index.html @@ -0,0 +1,5308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Arsenal - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Arsenal

+

When you click on "Arsenal" in the left-side bar, you access all the "Arsenal" tabs, visible on the top bar on the left. By default, the user directly access the "Malware" tab, but can navigate to the other tabs as well.

+

From the Arsenal section, users can access the following tabs:

+
    +
  • Malware: Malware represents any piece of code specifically designed to damage, disrupt, or gain unauthorized access to computer systems, networks, or user data.
  • +
  • Channels: Channels, in the context of cybersecurity, refer to places or means through which actors disseminate information. This category is used in particular in the context of FIMI (Foreign Information Manipulation Interference).
  • +
  • Tools: Tools represent legitimate, installed software or hardware applications on an operating system that can be misused by attackers for malicious purposes. (e.g. LOLBAS).
  • +
  • Vulnerabilities: Vulnerabilities are weaknesses or that can be exploited by attackers to compromise the security, integrity, or availability of a computer system or network.
  • +
+

Malware

+

General presentation

+

Malware encompasses a broad category of malicious pieces of code built, deployed, and operated by intrusion set. Malware can take many forms, including viruses, worms, Trojans, ransomware, spyware, and more. These entities are created by individuals or groups, including state-nations, state-sponsored groups, corporations, or hacktivist collectives.

+

Use the Malware SDO to model and track these threats comprehensively, facilitating in-depth analysis, response, and correlation with other security data.

+

When clicking on the Malware tab on the top left, you see the list of all the Malware you have access to, in respect with your allowed marking definitions. These malware are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, related intrusion sets, countries and sectors they target, and labels. You can then search and filter on some common and specific attributes of Malware.

+

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

+

The Malware cards

+

Visualizing Knowledge associated with a Malware

+

When clicking on an Malware card you land on its Overview tab. For a Malware, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Malware. Different thematic views are proposed to easily see the victimology, the threat actors and intrusion sets using the Malware, etc. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Channels

+

General presentation

+

Channels - such as forums, websites and social media platforms (e.g. Twitter, Telegram) - are mediums for disseminating news, knowledge, and messages to a broad audience. While they offer benefits like open communication and outreach, they can also be leveraged for nefarious purposes, such as spreading misinformation, coordinating cyberattacks, or promoting illegal activities.

+

Monitoring and managing content within Channels aids in analyzing threats, activities, and indicators associated with various threat actors, campaigns, and intrusion sets.

+

When clicking on the Channels tab at the top left, you see the list of all the Channels you have access to, in respect with your allowed marking definitions. These channels are displayed in a list where you can find certain fields characterizing the entity: type of channel, labels, and dates. You can then search and filter on some common and specific attributes of Channels.

+

Channels list

+

Visualizing Knowledge associated with a Channel

+

When clicking on a Channel in the list, you land on its Overview tab. For a Channel, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Channel. Different thematic views are proposed to easily see the victimology, the threat actors and intrusion sets using the Malware, etc. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Tools

+

General presentation

+

Tools refers to legitimate, pre-installed software applications, command-line utilities, or scripts that are present on a compromised system. These objects enable you to model and monitor the activities of these tools, which can be misused by attackers.

+

When clicking on the Tools tab at the top left, you see the list of all the Tools you have access to, in respect with your allowed marking definitions. These tools are displayed in a list where you can find certain fields characterizing the entity: labels and dates. You can then search and filter on some common and specific attributes of Tools.

+

Tools list

+

Visualizing Knowledge associated with an Observed Data

+

When clicking on a Tool in the list, you land on its Overview tab. For a Tool, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Tool. Different thematic views are proposed to easily see the threat actors, the intrusion sets and the malware using the Tool. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Vulnerabilities

+

General presentation

+

Vulnerabilities represent weaknesses or flaws in software, hardware, configurations, or systems that can be exploited by malicious actors. This object assists in managing and tracking the organization's security posture by identifying areas that require attention and remediation, while also providing insights into associated intrusion sets, malware and campaigns where relevant.

+

When clicking on the Vulnerabilities tab at the top left, you see the list of all the Vulnerabilities you have access to, in respect with your allowed marking definitions. These vulnerabilities are displayed in a list where you can find certain fields characterizing the entity: CVSS3 severity, labels, dates and creators (in the platform). You can then search and filter on some common and specific attributes of Vulnerabilities.

+

Vulnerabilities list

+

Visualizing Knowledge associated with an Observed Data

+

When clicking on a Vulnerabilities in the list, you land on its Overview tab. For a Vulnerability, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Vulnerability. Different thematic views are proposed to easily see the threat actors, the intrusion sets and the malware exploiting the Vulnerability. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-cases/index.html b/6.2.X/usage/exploring-cases/index.html new file mode 100755 index 00000000..17b13fa2 --- /dev/null +++ b/6.2.X/usage/exploring-cases/index.html @@ -0,0 +1,5233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cases - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Cases

+

When you click on "Cases" in the left-side bar, you access all the "Cases" tabs, visible on the top bar on the left. By default, the user directly access the "Incident Responses" tab, but can navigate to the other tabs as well.

+

As Analyses, Cases can contain other objects. This way, by adding context and results of your investigations in the case, you will be able to get an up-to-date overview of the ongoing situation, and later produce more easily an incident report.

+

From the Cases section, users can access the following tabs:

+
    +
  • Incident Responses: This type of Cases is dedicated to the management of incidents. An Incident Response case does not represent an incident, but all the context and actions that will encompass the response to a specific incident.
  • +
  • Request for Information: CTI teams are often asked to provide extensive information and analysis on a specific subject, be it related to an ongoing incident or a particular trending threat. Request for Information cases allow you to store context and actions relative to this type of request and its response.
  • +
  • Request for Takedown: When an organization is targeted by an attack campaign, a typical response action can be to request the Takedown of elements of the attack infrastructure, for example a domain name impersonating the organization to phish its employees, or an email address used to deliver phishing content. As Takedown needs in most case to reach out to external providers and be effective quickly, it often needs specific workflows. Request for Takedown cases give you a dedicated space to manage these specific actions.
  • +
  • Tasks: In every case, you need tasks to be performed in order to solve it. The Tasks tab allows you to review all created tasks to quickly see past due date, or quickly see every task assigned to a specific user.
  • +
  • Feedbacks: If you use your platform to interact with other teams and provide them CTI Knowledge, some users may want to give you feedback about it. Those feedbacks can easily be considered as another type of case to solve, as it will often refer to Knowledge inconsistency or gaps.
  • +
+

Cases Default page is Incident Response

+

Incident Response, Request for Information & Request for Takedown

+

General presentation

+

Incident responses, Request for Information & Request for Takedown cases are an important part of the case management system in OpenCTI. Here, you can organize the work of your team to respond to cybersecurity situations. You can also give context to the team and other users on the platform about the situation and actions (to be) taken.

+

To manage the situation, you can issue Tasks and assign them to users in the platform, by directly creating a Task or by applying a Case template that will append a list of predefined tasks.

+

To bring context, you can use your Case as a container (like Reports or Groupings), allowing you to add any Knowledge from your platform in it. You can also use this possibility to trace your investigation, your Case playing the role of an Incident report. You will find more information about case management here.

+

Incident Response, Request for Information & Request for Takedown are not STIX 2.1 Objects.

+

When clicking on the Incident Response, Request for Information & Request for Takedown tabs at the top, you see the list of all the Cases you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes.

+

Visualizing Knowledge within an Incident Response, Request for Information & Request for Takedown

+

When clicking on an Incident Response, Request for Information or Request for Takedown, you land on the Overview tab. The following tabs are accessible:

+
    +
  • Overview: Overview of Cases are slightly different from the usual (described here). Cases' Overview displays also the list of the tasks associated with the case. It also let you highlight Incident, Report or Sighting at the origin of the case. If other cases contains some Observables with your Case, they will be displayed as Related Cases in the Overview.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge contained in the Case, accessible through different views (See below for a dive-in). As described here.
  • +
  • Content: a tab to upload or creates outcomes document displaying the content of the Case (for example in PDF, text, HTML or markdown files). The Content of the document is displayed to ease the access of Knowledge through a readable format. As described here.
  • +
  • Entities: A table containing all SDO (Stix Domain Objects) contained in the Case, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • +
  • Observables: A table containing all SCO (Stix Cyber Observable) contained in the Case, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • +
  • Data: as described here.
  • +
+

Exploring and modifying the structured Knowledge contained in a Case can be done through different lenses.

+

Graph View

+

Graph View of a Case

+

In Graph view, STIX SDO are displayed as graph nodes and relationships as graph links. Nodes are colored depending on their type. Direct relationship are displayed as plain link and inferred relationships in dotted link. +At the top right, you will find a series of icons. From there you can change the current type of view. Here you can also perform global action on the Knowledge of the Case. Let's highlight 2 of them:

+
    +
  • Suggestions: This tool suggests you some logical relationships to add between your contained Object to give more consistency to your Knowledge.
  • +
  • Share with an Organization: if you have designated a main Organization in the platform settings, you can here share your Case and its content with users of another Organization. +At the bottom, you have many option to manipulate the graph:
  • +
  • Multiple option for shaping the graph and applying forces to the nodes and links
  • +
  • Multiple selection options
  • +
  • Multiple filters, including a time range selector allowing you to see the evolution of the Knowledge within the Case.
  • +
  • Multiple creation and edition tools to modify the Knowledge contained in the Case.
  • +
+

Content mapping view

+

Content mapping view of a Case

+

Through this view, you can map existing or new Objects directly from a readable content, allowing you to quickly append structured Knowledge in your Case before refining it with relationships and details. +This view is a great place to see the continuum between unstructured and structured Knowledge.

+

Timeline view

+

Timeline view of a Case

+

This view allows you to see the structured Knowledge chronologically. This view is particularly useful in the context of a Case, allowing you to see the chain of events, either from the attack perspectives, the defense perspectives or both. +The view can be filtered and displayed relationships too.

+

Matrix view

+

If your Case contains attack patterns, you will be able to visualize them in a Matrix view.

+

Tasks

+

Tasks are actions to be performed in the context of a Case (Incident Response, Request for Information, Request for Takedown). Usually, a task is assigned to a user, but important tasks may involve more participants.

+

When clicking on the Tasks tab at the top of the interface, you see the list of all the Tasks you have access to, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of the tasks.

+

Clicking on a Task, you land on its Overview tab. For a Tasks, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Feedbacks

+

When a user fill a feedback form from its Profile/Feedback menu, it will then be accessible here.

+

This feature gives the opportunity to engage with other users of your platform and to respond directly to their concern about it or the Knowledge, without the need of third party software.

+

Feedback Overview

+

Clicking on a Feedback, you land on its Overview tab. For a Feedback, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Content: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-entities/index.html b/6.2.X/usage/exploring-entities/index.html new file mode 100755 index 00000000..207519b6 --- /dev/null +++ b/6.2.X/usage/exploring-entities/index.html @@ -0,0 +1,5424 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Entities - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Entities

+

OpenCTI's Entities objects provides a comprehensive framework for modeling various targets and attack victims within your threat intelligence data. With five distinct Entity object types, you can represent sectors, events, organizations, systems, and individuals. This robust classification empowers you to contextualize threats effectively, enhancing the depth and precision of your analysis.

+

When you click on "Entities" in the left-side bar, you access all the "Entities" tabs, visible on the top bar on the left. By default, the user directly access the "Sectors" tab, but can navigate to the other tabs as well.

+

From the Entities section, users can access the following tabs:

+
    +
  • Sectors: areas of activity.
  • +
  • Events: event in the real world.
  • +
  • Organizations: groups with specific aims such as companies and government entities.
  • +
  • Systems: technologies such as platforms and software.
  • +
  • Individuals: real persons.
  • +
+

Sectors

+

General presentation

+

Sectors represent specific domains of activity, defining areas such as energy, government, health, finance, and more. Utilize sectors to categorize targeted industries or sectors of interest, providing valuable context for threat intelligence analysis within distinct areas of the economy.

+

When clicking on the Sectors tab at the top left, you see the list of all the Sectors you have access to, in respect with your allowed marking definitions.

+

Sectors list

+

Visualizing Knowledge associated with a Sector

+

When clicking on a Sector in the list, you land on its Overview tab. For a Sector, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Sector. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Sector. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Sector.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Sector overview

+

Events

+

General presentation

+

Events encompass occurrences like international sports events, summits (e.g., G20), trials, conferences, or any significant happening in the real world. By modeling events, you can analyze threats associated with specific occurrences, allowing for targeted investigations surrounding high-profile incidents.

+

When clicking on the Events tab at the top left, you see the list of all the Events you have access to, in respect with your allowed marking definitions.

+

Events list

+

Visualizing Knowledge associated with an Event

+

When clicking on an Event in the list, you land on its Overview tab. For an Event, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Event. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Event. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted during an attack against the Event.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Organizations

+

General presentation

+

Organizations include diverse entities such as companies, government bodies, associations, non-profits, and other groups with specific aims. Modeling organizations enables you to understand the threat landscape concerning various entities, facilitating investigations into cyber-espionage, data breaches, or other malicious activities targeting specific groups.

+

When clicking on the Organizations tab at the top left, you see the list of all the Organizations you have access to, in respect with your allowed marking definitions.

+

Organizations list

+

Visualizing Knowledge associated with an Organization

+

When clicking on an Organization in the list, you land on its Overview tab. For an Organization, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Organization. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Organization. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Organization.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Furthermore, an Organization can be observed from an "Author" perspective. It is possible to change this viewpoint to the right of the entity name, using the "Display as" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to "Author" mode, the observed data pertains to the entity's description as an author within the platform:

+
    +
  • Overview: The "Latest created relationships" and "Latest containers about the object" panels are replaced by the "Latest containers authored by this entity" panel.
  • +
  • Knowledge: A tab that presents an overview of the data authored by the Organization (i.e. counters and a graph).
  • +
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the Organization is the author.
  • +
+

Organization author view

+

Systems

+

General presentation

+

Systems represent software applications, platforms, frameworks, or specific tools like WordPress, VirtualBox, Firefox, Python, etc. Modeling systems allows you to focus on threats related to specific software or technology, aiding in vulnerability assessments, patch management, and securing critical applications.

+

When clicking on the Systems tab at the top left, you see the list of all the Systems you have access to, in respect with your allowed marking definitions.

+

Systems list

+

Visualizing Knowledge associated with a System

+

When clicking on a System in the list, you land on its Overview tab. For a System, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the System. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the System. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the System.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Furthermore, a System can be observed from an "Author" perspective. It is possible to change this viewpoint to the right of the entity name, using the "Display as" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to "Author" mode, the observed data pertains to the entity's description as an author within the platform:

+
    +
  • Overview: The "Latest created relationships" and "Latest containers about the object" panels are replaced by the "Latest containers authored by this entity" panel.
  • +
  • Knowledge: A tab that presents an overview of the data authored by the System (i.e. counters and a graph).
  • +
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the System is the author.
  • +
+

Individuals

+

General presentation

+

Individuals represent specific persons relevant to your threat intelligence analysis. This category includes targeted individuals, or influential figures in various fields. Modeling individuals enables you to analyze threats related to specific people, enhancing investigations into cyber-stalking, impersonation, or other targeted attacks.

+

When clicking on the Individuals tab at the top left, you see the list of all the Individuals you have access to, in respect with your allowed marking definitions.

+

Positions list

+

Visualizing Knowledge associated with an Individual

+

When clicking on an Individual in the list, you land on its Overview tab. For an Individual, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Individual. Different thematic views are proposed to easily see the related entities, the threats, the locations, etc. linked to the Individual. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in the Individual.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Furthermore, an Individual can be observed from an "Author" perspective. It is possible to change this viewpoint to the right of the entity name, using the "Display as" drop-down menu (see screenshot below). This different perspective is accessible in the Overview, Knowledge and Analyses tabs. When switched to "Author" mode, the observed data pertains to the entity's description as an author within the platform:

+
    +
  • Overview: The "Latest created relationships" and "Latest containers about the object" panels are replaced by the "Latest containers authored by this entity" panel.
  • +
  • Knowledge: A tab that presents an overview of the data authored by the Individual (i.e. counters and a graph).
  • +
  • Analyses: The list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) for which the Individual is the author.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-events/index.html b/6.2.X/usage/exploring-events/index.html new file mode 100755 index 00000000..20a93d3a --- /dev/null +++ b/6.2.X/usage/exploring-events/index.html @@ -0,0 +1,5216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Events - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Events

+

When you click on "Events" in the left-side bar, you access all the "Events" tabs, visible on the top bar on the left. By default, the user directly access the "Incidents" tab, but can navigate to the other tabs as well.

+

From the Events section, users can access the following tabs:

+
    +
  • Incidents: In OpenCTI, Incidents correspond to a negative event happening on an information system. This can include a cyberattack (intrusion, phishing, etc.), a consolidated security alert generated by a SIEM or EDR that need to be qualified, and so on. It can also refer to an information warfare attack in the context of countering disinformation.
  • +
  • Sightings: Sightings correspond to the event in which an Observable (IP, domain name, certificate, etc.) is detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or an EDR.
  • +
  • Observed Data: Observed Data has been added in OpenCTI by compliance with the STIX 2.1 standard. You can see it has a pseudo-container that contains Observables, like a line of firewall log for example. Currently, it is rarely used.
  • +
+

Incidents

+

General presentation

+

Incidents usually represents negative events impacting resources you want to protect, but local definitions can vary a lot, from a simple security events send by a SIEM to a massive scale supply chain attack impacting a whole activity sector.

+

In the MITRE STIX 2.1, the Incident SDO has not yet been finalized and is the object of important work as part of a forthcoming STIX Extension.

+

When clicking on the Incidents tab at the top left, you see the list of all the Incidents you have access to, in respect with your allowed marking definitions.

+

Incidents list

+

Visualizing Knowledge associated with an Incident

+

When clicking on an Incident in the list, you land on its Overview tab. For an Incident, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display two distribution graphs of its related Entities (STIX SDO) and Observable (STIX SCO).
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Incident. Different thematic views are proposed to easily see the victimology, arsenal, techniques and so on used in the context of the Incident. As described here.
  • +
  • Content: This specific tab allows to previzualize, manage and write deliverable associated with the Incident. For example an analytic report to share with other teams, a markdown files to feed a collaborative wiki with, etc. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Incident overview

+

Sightings

+

General presentation

+

The Sightings correspond to events in which an Observable (IP, domain name, url, etc.) is detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or EDR.

+

In OpenCTI, as we are in a cybersecurity context, Sightings are associated with Indicators of Compromise (IoC) and the notion of "True positive" and "False positive".

+

It is important to note that Sightings are a type of relationship (not a STIX SDO or STIX SCO), between an Observable and an Entities or Locations.

+

When clicking on the Sightings tab at the top left, you see the list of all the Sightings you have access to, in respect with your allowed marking definitions.

+

Sightings list

+

Visualizing Knowledge associated with a Sighting

+

When clicking on a Sighting in the list, you land on its Overview tab. As other relationships in the platform, Sighting's overview displays common related metadata, containers, external references, notes and entities linked by the relationship.

+

In addition, this overview displays: +- Qualification : if the Sighting is a True Positive or a False Positive +- Count : number of times the event has been seen

+

Observed Data

+

General presentation

+

Observed Data correspond to an extract from a log that contains Observables.

+

In the MITRE STIX 2.1, the Observed Data SDO is defined as such:

+
+

Observed Data conveys information about cybersecurity related entities such as files, systems, and networks using the STIX Cyber-observable Objects (SCOs). For example, Observed Data can capture information about an IP address, a network connection, a file, or a registry key. Observed Data is not an intelligence assertion, it is simply the raw information without any context for what it means.

+
+

When clicking on the Observed Data tab at the top left, you see the list of all the Observed Data you have access to, in respect with your allowed marking definitions.

+

Visualizing Knowledge associated with an Observed Data

+

When clicking on an Observed Data in the list, you land on its Overview tab. The following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display a distribution graphs of its related Observables (STIX SCO).
  • +
  • Entities : a sortable and filterable list of all Entities (SDO)
  • +
  • Observables: a sortable and filterable list of all Observables (SCO) in relation with the Observed Data
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-locations/index.html b/6.2.X/usage/exploring-locations/index.html new file mode 100755 index 00000000..1708027c --- /dev/null +++ b/6.2.X/usage/exploring-locations/index.html @@ -0,0 +1,5405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Locations - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Location

+

OpenCTI's Locations objects provides a comprehensive framework for representing various geographic entities within your threat intelligence data. With five distinct Location object types, you can precisely define regions, countries, areas, cities, and specific positions. This robust classification empowers you to contextualize threats geographically, enhancing the depth and accuracy of your analysis.

+

When you click on "Locations" in the left-side bar, you access all the "Locations" tabs, visible on the top bar on the left. By default, the user directly access the "Regions" tab, but can navigate to the other tabs as well.

+

From the Locations section, users can access the following tabs:

+
    +
  • Regions: very large geographical territories, such as a continent.
  • +
  • Countries: the world's countries.
  • +
  • Areas: more or less extensive geographical areas and often not having a very defined limit
  • +
  • Cities: the world's cities.
  • +
  • Positions: very precise positions on the globe.
  • +
+

Regions

+

General presentation

+

Regions encapsulate broader geographical territories, often representing continents or significant parts of continents. Examples include EMEA (Europe, Middle East, and Africa), Asia, Western Europe, and North America. Utilize regions to categorize large geopolitical areas and gain macro-level insights into threat patterns.

+

When clicking on the Regions tab at the top left, you see the list of all the Regions you have access to, in respect with your allowed marking definitions.

+

Regions list

+

Visualizing Knowledge associated with a Region

+

When clicking on a Region in the list, you land on its Overview tab. For a Region, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity of not having a Details section but a map locating the Region.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Region. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Region. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a Region.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Region Overview

+

Countries

+

General presentation

+

Countries represent individual nations across the world. With this object type, you can specify detailed information about a particular country, enabling precise localization of threat intelligence data. Countries are fundamental entities in geopolitical analysis, offering a focused view of activities within national borders.

+

When clicking on the Countries tab at the top left, you see the list of all the Countries you have access to, in respect with your allowed marking definitions.

+

Countries list

+

Visualizing Knowledge associated with a Country

+

When clicking on a Country in the list, you land on its Overview tab. For a Country, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity of not having a Details section but a map locating the Country.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Country. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Country. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a Country.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Areas

+

General presentation

+

Areas define specific geographical regions of interest, such as the Persian Gulf, the Balkans, or the Caucasus. Use areas to identify unique zones with distinct geopolitical, cultural, or strategic significance. This object type facilitates nuanced analysis of threats within defined geographic contexts.

+

When clicking on the Areas tab at the top left, you see the list of all the Areas you have access to, in respect with your allowed marking definitions.

+

Areas list

+

Visualizing Knowledge associated with an Area

+

When clicking on an Area in the list, you land on its Overview tab. For an Area, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity of not having a Details section but a map locating the Area.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Area. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Area. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in an Area.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Cities

+

General presentation

+

Cities provide granular information about urban centers worldwide. From major metropolises to smaller towns, cities are crucial in understanding localized threat activities. With this object type, you can pinpoint threats at the urban level, aiding in tactical threat assessments and response planning.

+

When clicking on the Cities tab at the top left, you see the list of all the Cities you have access to, in respect with your allowed marking definitions.

+

Cities list

+

Visualizing Knowledge associated with a City

+

When clicking on a City in the list, you land on its Overview tab. For a City, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity of not having a Details section but a map locating the City.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the City. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the City. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted in a City.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Positions

+

General presentation

+

Positions represent highly precise geographical points, such as monuments, buildings, or specific event locations. This object type allows you to define exact coordinates, enabling accurate mapping of events or incidents. Positions enhance the granularity of your threat intelligence data, facilitating precise geospatial analysis.

+

When clicking on the Positions tab at the top left, you see the list of all the Positions you have access to, in respect with your allowed marking definitions.

+

Positions list

+

Visualizing Knowledge associated with a Position

+

When clicking on a Position in the list, you land on its Overview tab. For a Position, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display a map locating the Position.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Position. Different thematic views are proposed to easily see the related entities, the threats, the incidents, etc. linked to the Position. As described here.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which an Indicator (IP, domain name, url, etc.) is sighted at a Position.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-observations/index.html b/6.2.X/usage/exploring-observations/index.html new file mode 100755 index 00000000..c0532ee7 --- /dev/null +++ b/6.2.X/usage/exploring-observations/index.html @@ -0,0 +1,5317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Observations - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Observations

+

When you click on "Observations" in the left-side bar, you access all the "Observations" tabs, visible on the top bar on the left. By default, the user directly access the "Observables" tab, but can navigate to the other tabs as well.

+

From the Observations section, users can access the following tabs:

+
    +
  • Observables: An Observable represents an immutable object. Observables can encompass a wide range of entities such as IPv4 addresses, domain names, email addresses, and more.
  • +
  • Artefacts: In OpenCTI, the Artefacts is a particular Observable. It may contain a file, such as a malware sample.
  • +
  • Indicators: An Indicator is a detection object. It is defined by a search pattern, which could be expressed in various formats such as STIX, Sigma, YARA, among others.
  • +
  • Infrastructures: An Infrastructure describes any systems, software services and any associated physical or virtual resources intended to support some purpose (e.g. C2 servers used as part of an attack, devices or servers that are part of defense, database servers targeted by an attack, etc.).
  • +
+

Observables

+

General presentation

+

An Observable is a distinct entity from the Indicator within OpenCTI and represents an immutable object. Observables can encompass a wide range of entities such as IPv4 addresses, domain names, email addresses, and more. Importantly, Observables don't inherently imply malicious intent, they can include items like legitimate IP addresses or domains associated with an organization. Additionally, they serve as raw data points without the additional detection context found in Indicators.

+

When clicking on the Observables tab at the top left, you see the list of all the Observables you have access to, in respect with your allowed marking definitions.

+

Observables list

+

Visualizing Knowledge associated with an Observable

+

When clicking on an Observable in the list, you land on its Overview tab. For an Observable, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display Indicators composed with the Observable.
  • +
  • Knowledge: a tab listing all its relationships and nested objects.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Observable (IP, domain name, url, etc.) has been sighted.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Observable overview

+

Artefacts

+

General presentation

+

An Artefact is a particular Observable. It may contain a file, such as a malware sample. Files can be uploaded or downloaded in encrypted archives, providing an additional layer of security against potential manipulation of malicious payloads.

+

When clicking on the Artefacts tab at the top left, you see the list of all the Artefacts you have access to, in respect with your allowed marking definitions.

+

Artefacts list

+

Visualizing Knowledge associated with an Artefact

+

When clicking on an Artefact in the list, you land on its Overview tab. For an Artefact, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to be able to download the attached file.
  • +
  • Knowledge: a tab listing all its relationships and nested objects.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Artefact has been sighted.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Artefact overview

+

Indicators

+

General presentation

+

An Indicator is a detection object. It is defined by a search pattern, which could be expressed in various formats such as STIX, Sigma, YARA, among others. This pattern serves as a key to identify potential threats within the data. Furthermore, an Indicator includes additional information that enriches its detection context. This information encompasses:

+
    +
  • Validity dates: Indicators are accompanied by a time frame, specifying the duration of their relevance, and modeled by the Valid from and Valid until dates.
  • +
  • Actionable fields: Linked to the validity dates, the Revoked and Detection fields can be used to sort Indicators for detection purposes.
  • +
  • Kill chain phase: They indicate the phase within the cyber kill chain where they are applicable, offering insights into the progression of a potential threat.
  • +
  • Types: Indicators are categorized based on their nature, aiding in classification and analysis.
  • +
+

When clicking on the Indicators tab at the top left, you see the list of all the Indicators you have access to, in respect with your allowed marking definitions.

+

Indicators list

+

Visualizing Knowledge associated with an Indicator

+

When clicking on an Indicator in the list, you land on its Overview tab. For an Indicator, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display the Observables on which it is based.
  • +
  • Knowledge: a tab listing all its relationships.
  • +
  • Analyses: as described here.
  • +
  • Sightings: a table containing all Sightings relationships corresponding to events in which the Indicator has been sighted.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Indicator overview

+

Infrastructures

+

General presentation

+

An Infrastructure refers to a set of resources, tools, systems, or services employed by a threat to conduct their activities. It represents the underlying framework or support system that facilitates malicious operations, such as the command and control (C2) servers in an attack. Notably, like Observables, an Infrastructure doesn't inherently imply malicious intent. It can also represent legitimate resources affiliated with an organization (e.g. devices or servers that are part of defense, database servers targeted by an attack, etc.).

+

When clicking on the Infrastructures tab at the top left, you see the list of all the Infrastructures you have access to, in respect with your allowed marking definitions.

+

Infrastructures list

+

Visualizing Knowledge associated with an Infrastructure

+

When clicking on an Infrastructure in the list, you land on its Overview tab. For an Infrastructure, the following tabs are accessible:

+
    +
  • Overview: as described here, with the particularity to display distribution graphs of its related Observable (STIX SCO).
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Infrastructure. Different thematic views are proposed to easily see the threats, the arsenal, the observations, etc. linked to the Infrastructure. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-techniques/index.html b/6.2.X/usage/exploring-techniques/index.html new file mode 100755 index 00000000..9c50bcf0 --- /dev/null +++ b/6.2.X/usage/exploring-techniques/index.html @@ -0,0 +1,5327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Techniques - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Techniques

+

When you click on "Techniques" in the left-side bar, you access all the "Techniques" tabs, visible on the top bar on the left. By default, the user directly access the "Attack pattern" tab, but can navigate to the other tabs as well.

+

From the Techniques section, users can access the following tabs:

+
    +
  • Attack pattern: attacks pattern used by the threat actors to perform their attacks. By default, OpenCTI is provisionned with attack patterns from MITRE ATT&CK matrices (for CTI) and DISARM matrix (for FIMI).
  • +
  • Narratives: In OpenCTI, narratives used by threat actors can be represented and linked to other Objects. Narratives are mainly used in the context of disinformation campaigns where it is important to trace which narratives have been and are still used by threat actors.
  • +
  • Courses of action: A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress. It may describe technical, automatable responses (applying patches, reconfiguring firewalls) but can also describe higher level actions like employee training or policy changes. For example, a course of action to mitigate a vulnerability could describe applying the patch that fixes it.
  • +
  • Data sources: Data sources represent the various subjects/topics of information that can be collected by sensors/logs. Data sources also include data components,
  • +
  • Data components: Data components identify specific properties/values of a data source relevant to detecting a given ATT&CK technique or sub-technique.
  • +
+

Attack pattern

+

General presentation

+

Attacks pattern used by the threat actors to perform their attacks. By default, OpenCTI is provisionned with attack patterns from MITRE ATT&CK matrices and CAPEC (for CTI) and DISARM matrix (for FIMI).

+

In the MITRE STIX 2.1 documentation, an Attack pattern is defined as such :

+
+

Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets. Attack Patterns are used to help categorize attacks, generalize specific attacks to the patterns that they follow, and provide detailed information about how attacks are performed. An example of an attack pattern is "spear phishing": a common type of attack where an attacker sends a carefully crafted e-mail message to a party with the intent of getting them to click a link or open an attachment to deliver malware. Attack Patterns can also be more specific; spear phishing as practiced by a particular threat actor (e.g., they might generally say that the target won a contest) can also be an Attack Pattern.

+
+

When clicking on the Attack pattern tab at the top left, you access the list of all the attack pattern you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of attack patterns.

+

Visualizing Knowledge associated with an Attack pattern

+

When clicking on an Attack pattern, you land on its Overview tab. For an Attack pattern, the following tabs are accessible:

+
    +
  • +

    Overview: Overview of Attack pattern is a bit different as the usual described here. The "Details" box is more structured and contains information about:

    +
  • +
  • +

    parent or subtechniques (as in the MITRE ATT&CK matrices),

    +
  • +
  • related kill chain phases
  • +
  • Platform on which the Attack pattern is usable,
  • +
  • permission required to apply it
  • +
  • Related detection technique
  • +
  • Courses of action to mitigate the Attack pattern
  • +
  • Data components in which find data to detect the usage of the Attack pattern
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Attack pattern. Different thematic views are proposed to easily see Threat Actors and Intrusion Sets using this techniques, linked incidents, etc.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Narratives

+

General presentation

+

In OpenCTI, narratives used by threat actors can be represented and linked to other Objects. Narratives are mainly used in the context of disinformation campaigns where it is important to trace which narratives have been and are still used by threat actors.

+

An example of Narrative can be "The country A is weak and corrupted" or "The ongoing operation aims to free people".

+

Narrative can be a mean in the context of a more broad attack or the goal of the operation, a vision to impose.

+

When clicking on the Narrative tab at the top left, you access the list of all the Narratives you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of narratives.

+

Visualizing Knowledge associated with a Narrative

+

When clicking on a Narrative, you land on its Overview tab. For a Narrative, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Narratives. Different thematic views are proposed to easily see the Threat actors and Intrusion Set using the Narrative, etc.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Courses of action

+

General presentation

+

In the MITRE STIX 2.1 documentation, an Course of action is defined as such :

+
+

A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress. It may describe technical, automatable responses (applying patches, reconfiguring firewalls) but can also describe higher level actions like employee training or policy changes. For example, a course of action to mitigate a vulnerability could describe applying the patch that fixes it.

+
+

When clicking on the Courses of action tab at the top left, you access the list of all the Courses of action you have access too, in respect with your allowed marking definitions. You can then search and filter on some common and specific attributes of course of action.

+

Visualizing Knowledge associated with a Course of action

+

When clicking on a Course of Action, you land on its Overview tab. For a Course of action, the following tabs are accessible:

+
    +
  • Overview: Overview of Course of action is a bit different as the usual described here. In "Details" box, mitigated attack pattern are listed.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Narratives. Different thematic views are proposed to easily see the Threat actors and Intrusion Set using the Narrative, etc.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Data sources & Data components

+

General presentation

+

In the MITRE ATT&CK documentation, Data sources are defined as such :

+
+

Data sources represent the various subjects/topics of information that can be collected by sensors/logs. Data sources also include data components, which identify specific properties/values of a data source relevant to detecting a given ATT&CK technique or sub-technique.

+
+

Visualizing Knowledge associated with a Data source or a Data components

+

When clicking on a Data source or a Data component, you land on its Overview tab. For a Course of action, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/exploring-threats/index.html b/6.2.X/usage/exploring-threats/index.html new file mode 100755 index 00000000..825444ec --- /dev/null +++ b/6.2.X/usage/exploring-threats/index.html @@ -0,0 +1,5214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Threats - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Threats

+

When you click on "Threats" in the left-side bar, you access all the "Threats" tabs, visible on the top bar on the left. By default, the user directly access the "Threat Actor (Group)" tab, but can navigate to the other tabs as well.

+

From the Threats section, users can access the following tabs:

+
    +
  • Threat actors (Group): Threat actor (Group) represents a physical group of attackers operating an Intrusion set, using malware and attack infrastructure, etc.
  • +
  • Threat actors (Indvidual): Threat actor (Individual) represents a real attacker that can be described by physical and personal attributes and motivations. Threat actor (Individual) operates Intrusion set, uses malware and infrastructure, etc.
  • +
  • Intrusion sets: Intrusion set is an important concept in Cyber Threat Intelligence field. It is a consistent set of technical and non-technical elements corresponding of what, how and why a Threat actor acts. it is particularly useful for associating multiple attacks and malicious actions to a defined Threat, even without sufficient information regarding who did them. Often, with you understanding of the threat growing, you will link an Intrusion set to a Threat actor (either a Group or an Individual).
  • +
  • Campaigns: Campaign represents a series of attacks taking place in a certain period of time and/or targeting a consistent subset of Organization/Individual.
  • +
+

Threat actors (Group and Individual)

+

General presentation

+

Threat actors are the humans who are building, deploying and operating intrusion sets. A threat actor can be an single individual or a group of attackers (who may be composed of individuals). A group of attackers may be a state-nation, a state-sponsored group, a corporation, a group of hacktivists, etc.

+

Beware, groups of attackers might be modelled as "Intrusion sets" in feeds, as there is sometimes a misunderstanding in the industry between group of people and the technical/operational intrusion set they operate.

+

The Threat actor (Group) cards

+

When clicking on the Threat actor (Group or Individual) tabs at the top left, you see the list of all the groups of Threat actors or Individual Threat actors you have access to, in respect with your allowed marking definitions. These groups or individual are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware they used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Threat actors.

+

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

+

Demographic and Biographic Information

+

Individual Threat actors have unique properties to represent demographic and biographic information. Currently tracked demographics include their countries of residence, citizenships, date of birth, gender, and more.

+

Threat Actor (Individual) Demographics

+

Biographic information includes their eye and hair color, as well as known heights and weights.

+

Threat Actor (Individual) Biographics

+

An Individual Threat actor can also be tracked as employed by an Organization or a Threat Actor group. This relationship can be set under the knowledge tab.

+

Visualizing Knowledge associated with a Threat actor

+

When clicking on a Threat actor Card, you land on its Overview tab. For a Threat actor, the following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Threat actor. Different thematic views are proposed to easily see the victimology, arsenal and techniques used by the Threat actor, etc. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Intrusion Sets

+

An intrusion set is a consistent group of technical elements such as "tactics, technics and procedures" (TTP), tools, malware and infrastructure used by a threat actor against one or a number of victims who are usually sharing some characteristics (field of activity, country or region) to reach a similar goal whoever the victim is. The intrusion set may be deployed once or several times and may evolve with time. +Several intrusion sets may be linked to one threat actor. All the entities described below may be linked to one intrusion set. There are many debates in the Threat Intelligence community on how to define an intrusion set and how to distinguish several intrusion sets with regards to:

+
    +
  • their differences
  • +
  • their evolutions
  • +
  • the possible reuse
  • +
  • "false flag" type of attacks
  • +
+

As OpenCTI is very customizable, each organization or individual may use these categories as they wish. Instead, it is also possible to use the import feed for the choice of categories.

+

Intrusion set Cards

+

When clicking on the Intrusion set tab on the top left, you see the list of all the Intrusion sets you have access to, in respect with your allowed marking definitions. These intrusion sets are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware they used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Intrusion set.

+

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

+

Visualizing Knowledge associated with an Intrusion set

+

When clicking on an Intrusion set Card, you land on its Overview tab. The following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Intrusion Set. Different thematic views are proposed to easily see the victimology, arsenal and techniques used by the Intrusion Set, etc. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+

Campaigns

+

A campaign can be defined as "a series of malicious activities or attacks (sometimes called a "wave of attacks") taking place within a limited period of time, against a defined group of victims, associated to a similar intrusion set and characterized by the use of one or several identical malware towards the various victims and common TTPs". +However, a campaign is an investigation element and may not be widely recognized. Thus, a provider might define a series of attacks as a campaign and another as an intrusion set. +Campaigns can be attributed to an Intrusion set.

+

Campaigns cards

+

When clicking on the Campaign tab on the top left, you see the list of all the Campaigns you have access to, in respect with your allowed marking definitions. These campaigns are displayed as Cards where you can find a summary of the important Knowledge associated with each of them: description, aliases, malware used, countries and industries they target, labels. You can then search and filter on some common and specific attributes of Campaigns.

+

At the top right of each Card, you can click the star icon to put it as favorite. It will pin the card on top of the list. You will also be able to display all your favorite easily in your Custom Dashboards.

+

Visualizing Knowledge associated with a Campaign

+

When clicking on an Campaign Card, you land on its Overview tab. The following tabs are accessible:

+
    +
  • Overview: as described here.
  • +
  • Knowledge: a complex tab that regroups all the structured Knowledge linked to the Campaign. Different thematic views are proposed to easily see the victimology, arsenal and techniques used in the context of the Campaign. As described here.
  • +
  • Analyses: as described here.
  • +
  • Data: as described here.
  • +
  • History: as described here.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/export/index.html b/6.2.X/usage/export/index.html new file mode 100755 index 00000000..95cdb9db --- /dev/null +++ b/6.2.X/usage/export/index.html @@ -0,0 +1,5124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Manual export - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Manual export

+

Introduction

+

With the OpenCTI platform, you can manually export your intelligence content in the following formats:

+
    +
  • JSON,
  • +
  • CSV,
  • +
  • PDF,
  • +
  • TXT.
  • +
+

Generate an export panel

+

Export in structured or document format

+

+

Generate an export

+

To export one or more entities you have two possibilities. First you can click on the button "Open export panel". The list of pre-existing exports will open, and in the bottom right-hand corner you can configure and generate a new export.

+

open export panel +This opens the export settings panel, where you can customize your export according to four fields:

+
    +
  • desired export format (text/csv, application/pdf, application/vnd.oasis.stix+json, text/plain)
  • +
  • export type (simple or full),
  • +
  • the max marking definition levels of the elements to be included in the export (a TLP level, for instance). The list of the available max markings is limited by the user allowed markings and its maximum shareable markings (more details about maximum shareable marking definitions in data segregation). For a marking definition type to be taken into account here, a marking definition from this type must be provided. For example, if you select TLP:GREEN for this field, AMBER and RED elements will be excluded but it will not take into account any PAP markings unless one is elected too.
  • +
  • the file marking definition levels of the export (a TLP level, for instance). This marking on the file itself will then restrain the access to it in accordance with users' marking definition levels. For example, if a file has the marking TLP:RED and INTERNAL, a user will need to have these marking to see and access the file in the platform.```
  • +
+

customize your export

+

The second way is to click directly on the "Generate an Export" button to export the content of an entity in the desired format. The same settings panel will open.

+

Export entity content

+

Both ways add your export in the Exported files list in the Data tab. +Exported files list

+

Export possibilities

+

All entities in your instance can be exported either directly via Generate Export or indirectly via Export List in .json and .csv formats.

+

Export a list of entities

+

You have the option to export either a single element, such as a report, or a collection of elements, such as multiple reports. These exports may contain not only the entity itself but also related elements, depending on the type of export you select: "simple" or "full". See the Export types (simple and full) section.

+

You can also choose to export a list of entities within a container. To do so, go to the container's entities tab. For example, for a report, if you only want to retrieve entity type attack pattern and indicators to design a detection strategy, go to the entities tab and select specific elements for export.

+

Export specific elements

+

+

Export types (simple and full)

+

When you wish to export only the content of a specific entity such as a report, you can choose a "simple" export type.

+

If you also wish to export associated content, you can choose a "full" export. With this type of export, the entity will be exported along with all entities directly associated with the central one (first neighbors).

+

Export types

+

Exports list panel

+

Once an export has been created, you can find it in the export list panel. Simply click on a particular export to download it.

+

You can also generate a new export directly from the Exports list, as explained in the Generate an export section.

+

Exports list

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/feeds/index.html b/6.2.X/usage/feeds/index.html new file mode 100755 index 00000000..86724b95 --- /dev/null +++ b/6.2.X/usage/feeds/index.html @@ -0,0 +1,5150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Native feeds - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Native feeds

+

OpenCTI provides versatile mechanisms for sharing data through its built-in feeds, including Live streams, TAXII collections, and CSV feeds.

+

Feed configuration

+

Feeds are configured in the "Data > Data sharing" window. Configuration for all feed types is uniform and relies on the following parameters:

+
    +
  • Filter setup: The feed can have specific filters to publish only a subset of the platform overall knowledge. Any data that meets the criteria established by the user's feed filters will be shared (e.g. specific types of entities, labels, marking definitions, etc.).
  • +
  • Access control: A feed can be either public, i.e. accessible without authentication, or restricted. By default, it's accessible to any user with the "Access data sharing" capability, but it's possible to increase restrictions by limiting access to a specific user, group, or organization.
  • +
+

Feed access restriction

+

By carefully configuring filters and access controls, you can tailor the behavior of Live streams, TAXII collections, and CSV feeds to align with your specific data-sharing needs.

+

+

Live streams

+

Introduction

+

Live streams, an exclusive OpenCTI feature, increase the capacity for real-time data sharing by serving STIX 2.1 bundles as TAXII collections with advanced capabilities. What distinguishes them is their dynamic nature, which includes the creation, updating, and deletion of data. Unlike TAXII, Live streams comprehensively resolve relationships and dependencies, ensuring a more nuanced and interconnected exchange of information. This is particularly beneficial in scenarios where sharing involves entities with complex relationships, providing a richer context for the shared data.

+

In scenarios involving data sharing between two OpenCTI platforms, Live streams emerge as the preferred mechanism. These streams operate like TAXII collections but are notably enhanced, supporting:

+
    +
  • create, update and delete events depending on the parameters,
  • +
  • caching already created entities in the last 5 minutes,
  • +
  • resolving relationships and dependencies even out of the filters,
  • +
  • can be public (without authentication).
  • +
+

Live stream

+
+

Resolve relationships and dependencies

+

Dependencies and relationships of entities shared via Live streams, as determined by specified filters, are automatically shared even beyond the confines of these filters. This means that interconnected data, which may not directly meet the filter criteria, is still included in the Live stream. However, OpenCTI data segregation mechanisms are still applied. They allow restricting access to shared data based on factors such as markings or organization. It's imperative to carefully configure and manage these access controls to ensure that no confidential data is shared.

+
+

Illustrative scenario

+

To better understand how live streams are working, let's take a few examples, from simple to complex.

+

Given a live stream with filters Entity type: Indicator AND Label: detection. Let's see what happens with an indicator with:

+
    +
  • Marking definition: TLP:GREEN
  • +
  • Author Crowdstrike
  • +
  • Relation indicates to the malware Emotet
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ActionResult in stream (with Avoid dependencies resolution=true)Result in stream (with Avoid dependencies resolution=false)
1. Create an indicatorNothingNothing
2. Add the label detectionCreate TLP:GREEN, create CrowdStrike, create the indicatorCreate TLP:GREEN, create CrowdStrike, create the malware Emotet, create the indicator, create the relationship indicates
3. Remove the label detectionDelete the indicatorDelete the indicator and the relationship
4. Add the label detectionCreate the indicatorCreate the indicator, create the relationship indicates
5. Delete the indicatorDelete the indicatorDelete the indicator and the relationship
+

Details on how to consume these Live streams can be found on the dedicated page.

+

TAXII Collections

+

OpenCTI has an embedded TAXII API endpoint which provides valid STIX 2.1 bundles. If you wish to know more about the TAXII standard, please read the official introduction.

+

In OpenCTI you can create as many TAXII 2.1 collections as needed.

+

TAXII Collection

+

After creating a new collection, every system with a proper access token can consume the collection using different kinds of authentication (basic, bearer, etc.).

+

As when using the GraphQL API, TAXII 2.1 collections have a classic pagination system that should be handled by the consumer. Also, it's important to understand that element dependencies (nested IDs) inside the collection are not always contained/resolved in the bundle, so consistency needs to be handled at the client level.

+

+

CSV feeds

+

The CSV feed facilitates the automatic generation of a CSV file, accessible via a URL. The CSV file is regenerated and updated at user-defined intervals, providing flexibility. The entries in the file correspond to the information that matches the filters applied and that were created or modified in the platform during the time interval (between the last generation of the CSV and the new one).

+

CSV feed

+
+

CSV size limit

+

The CSV file generated has a limit of 5 000 entries. If more than 5 000 entities are retrieved by the platform, only the most recent 5 000 will be shared in the file.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/getting-started/index.html b/6.2.X/usage/getting-started/index.html new file mode 100755 index 00000000..5a9a04d8 --- /dev/null +++ b/6.2.X/usage/getting-started/index.html @@ -0,0 +1,5101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting started - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Getting started

+

This guide aims to give you a full overview of the OpenCTI features and workflows. The platform can be used in various contexts to handle threats management use cases from a technical to a more strategic level. OpenCTI has been designed as a knowledge graph, taking inputs (threat intelligence feeds, sightings & alerts, vulnerabilities, assets, artifacts, etc.) and generating outputs based on built-in capabilities and / or connectors.

+

Here are some examples of use cases:

+
    +
  • Cyber Threat Intelligence knowledge base
  • +
  • Detection as code feeds for XDR, EDR, SIEMs, firewalls, proxies, etc.
  • +
  • Incident response artifacts & cases management
  • +
  • Vulnerabilities management
  • +
  • Reporting, alerting and dashboarding on a subset of data
  • +
+

Use Cases

+

+

Welcome dashboard

+

The welcome page gives any visitor on the OpenCTI platform an overview of what's happening on the platform. It can be replaced by a custom dashboard, created by a user (or the default dashboard set up in a role, a group or an organization).

+

Dashboard

+

Indicators in the dashboard

+

Numbers

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentDescription
Intrusion setsNumber of intrusion sets .
MalwareNumber of malware.
ReportsNumber of reports.
IndicatorsNumber of indicators.
+

Charts & lists

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentDescription
Most active threats (3 last months)Top active threats (threat actor, intrusion set and campaign) during the last 3 months.
Most targeted victims (3 last months)Intensity of the targeting tied to the number of relations targets for a given entities (organization, sector, location, etc.) during the last 3 months.
Relationships createdVolume of relationships created over the past 12 months.
Most active malware (3 last months)Top active malware during the last 3 months.
Most active vulnerabilities (3 last months)List of the vulnerabilities with the greatest number of relations over the last 3 months.
Targeted countries (3 last months)Intensity of the targeting tied to the number of relations targets for a given country over the past 3 months.
Latest reportsLast reports ingested in the platform.
Most active labels (3 last months)Top labels given to entities during the last 3 months.
+
+

Explore the platform

+

To start exploring the platform and understand how information is structured, we recommend starting with the overview documentation page.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/import-automated/index.html b/6.2.X/usage/import-automated/index.html new file mode 100755 index 00000000..a9fe2d50 --- /dev/null +++ b/6.2.X/usage/import-automated/index.html @@ -0,0 +1,5281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Automated import - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Automated import

+

Automated imports in OpenCTI streamline the process of data ingestion, allowing users to effortlessly bring in valuable intelligence from diverse sources. This page focuses on the automated methods of importing data, which serve as bridges between OpenCTI and diverse external systems, formatting it into a STIX bundle, and importing it into the OpenCTI platform.

+

Connectors

+

Connectors in OpenCTI serve as dynamic gateways, facilitating the import of data from a wide array of sources and systems. Every connector is designed to handle specific data types and structures of the source, allowing OpenCTI to efficiently ingest the data.

+

Connector behaviors

+

The behavior of each connector is defined by its development, determining the types of data it imports and its configuration options. This flexibility allows users to customize the import process to their specific needs, ensuring a seamless and personalized data integration experience.

+

The level of configuration granularity regarding the imported data type varies with each connector. Nevertheless, connectors empower users to specify the date from which they wish to fetch data. This capability is particularly useful during the initial activation of a connector, enabling the retrieval of historical data. Following this, the connector operates in real-time, continuously importing new data from the source.

+

Connector Ecosystem

+

OpenCTI's connector ecosystem covers a broad spectrum of sources, enhancing the platform's capability to integrate data from various contexts, from threat intelligence providers to specialized databases. The list of available connectors can be found in our connectors catalog. Connectors are categorized into three types: import connectors (the focus here), enrichment connectors, and stream consumers. Further documentation on connectors is available on the dedicated documentation page.

+

In summary, automated imports through connectors empower OpenCTI users with a scalable, efficient, and customizable mechanism for data ingestion, ensuring that the platform remains enriched with the latest and most relevant intelligence.

+

Native automated import

+

In OpenCTI, the "Data > Ingestion" section provides users with built-in functions for automated data import. These functions are designed for specific purposes and can be configured to seamlessly ingest data into the platform. Here, we'll explore the configuration process for the four built-in functions: Live Streams, TAXII Feeds, RSS Feeds, and CSV Feeds.

+

Live streams

+

Live Streams enable users to consume data from another OpenCTI platform, fostering collaborative intelligence sharing. Here's a step-by-step guide to configure Live streams synchroniser:

+
    +
  1. Remote OpenCTI URL: Provide the URL of the remote OpenCTI platform (e.g., https://[domain]; don't include the path).
  2. +
  3. Remote OpenCTI token: Provide the user token. An administrator from the remote platform must supply this token, and the associated user must have the "Access data sharing" privilege.
  4. +
  5. After filling in the URL and user token, validate the configuration.
  6. +
  7. Once validated, select a live stream to which you have access.
  8. +
+

Live stream configuration

+

Additional configuration options:

+
    +
  • User responsible for data creation: Define the user responsible for creating data received from this stream. Best practice is to dedicate one user per source for organizational clarity. Please see the section "Best practices" below for more information.
  • +
  • Starting synchronization: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • +
  • Take deletions into account: Enable this option to delete data from your platform if it was deleted on the providing stream. (Note: Data won't be deleted if another source has imported it previously.)
  • +
  • Verify SSL certificate: Check the validity of the certificate of the domain hosting the remote platform.
  • +
  • Avoid dependencies resolution: Import only entities without their relationships. For instance, if the stream shares malware, all the malware's relationships will be retrieved by default. This option enables you to choose not to recover them.
  • +
  • Use perfect synchronization: This option is specifically for synchronizing two platforms. If an imported entity already exists on the platform, the one from the stream will overwrite it.
  • +
+

Live stream additional configuration

+

TAXII feeds

+

TAXII Feeds in OpenCTI provide a robust mechanism for ingesting TAXII collections from TAXII servers or other OpenCTI instances. Configuring TAXII ingester involves specifying essential details to seamlessly integrate threat intelligence data. Here's a step-by-step guide to configure TAXII ingesters:

+
    +
  1. TAXII server URL: Provide the root API URL of the TAXII server. For collections from another OpenCTI instance, the URL is in the form https://[domain]/taxii2/root.
  2. +
  3. TAXII collection: Enter the ID of the TAXII collection to be ingested. For collections from another OpenCTI instance, the ID follows the format 426e3acb-db50-4118-be7e-648fab67c16c.
  4. +
  5. Authentication type (if necessary): Enter the authentication type. For non-public collections from another OpenCTI instance, the authentication type is "Bearer token." Enter the token of a user with access to the collection (similar to the point 2 of the Live streams configuration above).
  6. +
+
+

TAXII root API URL

+

Many ISAC TAXII configuration instructions will provide the URL for the collection or discovery service. In these cases, remove the last path segment from the TAXII Server URL in order to use it in OpenCTI. eg. use https://[domain]/tipapi/tip21, and not https://[domain]/tipapi/tip21/collections.

+
+

Additional configuration options:

+
    +
  • User responsible for data creation: Define the user responsible for creating data received from this TAXII feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section "Best practices" below for more information.
  • +
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • +
+

TAXII feed configuration

+

RSS feeds

+

RSS Feeds functionality enables users to seamlessly ingest items in report form from specified RSS feeds. Configuring RSS Feeds involves providing essential details and selecting preferences to tailor the import process. Here's a step-by-step guide to configure RSS ingesters:

+
    +
  1. RSS Feed URL: Provide the URL of the RSS feed from which items will be imported.
  2. +
+

Additional configuration options:

+
    +
  • User responsible for data creation: Define the user responsible for creating data received from this RSS feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section "Best practices" below for more information.
  • +
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • +
  • Default report types: Indicate the report type to be applied to the imported report.
  • +
  • Default author: Indicate the default author to be applied to the imported report. Please see the section "Best practices" below for more information.
  • +
  • Default marking definitions: Indicate the default markings to be applied to the imported reports.
  • +
+

RSS feed configuration

+

CSV feeds

+

CSV feed ingester enables users to import CSV files exposed on URLs. Here's a step-by-step guide to configure TAXII ingesters:

+
    +
  1. CSV URL: Provide the URL of the CSV file exposed from which items will be imported.
  2. +
  3. CSV Mappers: Choose the CSV mapper to be used to import the data.
  4. +
  5. Authentication type (if necessary): Enter the authentication type.
  6. +
+
+

CSV mapper

+

CSV feed functionality is based on CSV mappers. It is necessary to create the appropriate CSV mapper to import the data contained in the file. See the page dedicated to the CSV mapper.

+
+

Additional configuration options:

+
    +
  • User responsible for data creation: Define the user responsible for creating data received from this CSV feed. Best practice is to dedicate one user per source for organizational clarity. Please see the section "Best practices" below for more information.
  • +
  • Import from date: Specify the date of the oldest data to retrieve. Leave the field empty to import everything.
  • +
+

CSV feeds creation: prior CSV mapper test

+

in CSV Mappers, if you created a representative for Marking definition, you could have chosen between 2 options:

+
    +
  • let the user choose marking definitions
  • +
  • Use default marking definitions of the user
  • +
+

This configuration applies when using a CSV Mapper for a CSV Ingester. If you select a CSV Mapper containing the option "Use default marking definitions of the user", the default marking definitions of the user you chose to be responsible for the data creation will be applied to all data imported. If you select a CSV Mapper containing the option "let the user choose marking definitions", you will be presented with the list of all the marking definitions of the user you chose to be responsible for the data creation (and not yours!)

+

To finalize the creation, click on "Verify" to run a check on the submitted URL with the selected CSV mapper. A valid URL-CSV mapper combination results in the identification of up to 50 entities.

+

CSV feeds creation: CSV mapper test

+

CSV feeds creation: list

+

To start your new ingester, click on "Start", in the burger menu.

+

CSV feeds creation: start

+

CSV feed ingestion is made possible thanks to the connector "ImportCSV". So you can track the progress in "Data > Ingestion > Connectors". On a regular basis, the ingestion is updated when new data is added to the CSV feed.

+

CSV feeds creation: connectors

+

CSV feeds creation: tracking

+

+

Best practices for feed import

+

Ensuring a secure and well-organized environment is paramount in OpenCTI. Here are two recommended best practices to enhance security, traceability, and overall organizational clarity:

+
    +
  1. Create a dedicated user for each source: Generate a user specifically for feed import, following the convention [F] Source name for clear identification. Assign the user to the "Connectors" group to streamline user management and permission related to data creation. Please see here for more information on this good practice.
  2. +
  3. Establish a dedicated Organization for the source: Create an organization named after the data source for clear identification. Assign the newly created organization to the "Default author" field in feed import configuration if available.
  4. +
+

By adhering to these best practices, you ensure independence in managing rights for each import source through dedicated user and organization structures. In addition, you enable clear traceability to the entity's creator, facilitating source evaluation, dashboard creation, data filtering and other administrative tasks.

+

Digest

+

Users can streamline the data ingestion process using various automated import capabilities. Each method proves beneficial in specific circumstances.

+
    +
  • Connectors act as bridges to retrieve data from diverse sources and format it for seamless ingestion into OpenCTI.
  • +
  • Live Streams enable collaborative intelligence sharing across OpenCTI instances, fostering real-time updates and efficient data synchronization.
  • +
  • TAXII Feeds provide a standardized mechanism for ingesting threat intelligence data from TAXII servers or other OpenCTI instances.
  • +
  • RSS Feeds facilitate the import of items in report form from specified RSS feeds, offering a straightforward way to stay updated on relevant intelligence.
  • +
+

By leveraging these automated import functionalities, OpenCTI users can build a comprehensive, up-to-date threat intelligence database. The platform's adaptability and user-friendly configuration options ensure that intelligence workflows remain agile, scalable, and tailored to the unique needs of each organization.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/import-files/index.html b/6.2.X/usage/import-files/index.html new file mode 100755 index 00000000..28863f28 --- /dev/null +++ b/6.2.X/usage/import-files/index.html @@ -0,0 +1,5211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Import from files - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Import from files

+

Import mechanisms

+

The platform provides a seamless process for automatically parsing data from various file formats. This capability is facilitated by two distinct mechanisms.

+

File import connectors: Currently, there are two connectors designed for importing files and automatically identifying entities.

+
    +
  • ImportFileStix: Designed to handle STIX-structured files (json or xml format).
  • +
  • ImportDocument: Versatile connector supporting an array of file formats, including pdf, text, html, and markdown.
  • +
+

CSV mappers: The CSV mapper is a tailored functionality to facilitate the import of data stored in CSV files. For more in-depth information on using CSV mappers, refer to the CSV Mappers documentation page.

+

Manual import connectors

+

Usage

+

Locations

+

Both mechanisms can be employed wherever file uploads are possible. This includes the "Data" tabs of all entities and the dedicated panel named "Data import and analyst workbenches" located in the top right-hand corner (database logo with a small gear). Importing files from these two locations is not entirely equal; refer to the "Relationship handling from entity's Data tab" section below for details on this matter.

+

Entity identification process

+

For ImportDocument connector, the identification process involves searching for existing entities in the platform and scanning the document for relevant information. In additions, the connector use regular expressions (regex) to detect IP addresses and domains within the document.

+

As for the ImportFileStix connector and the CSV mappers, there is no identification mechanism. The imported data will be, respectively, the data defined in the STIX bundle or according to the configuration of the CSV mapper used.

+

Workflow overview

+
    +
  1. Upload file: Navigate to the desired location, such as the "Data" tabs of an entity or the "Data import and analyst workbenches" panel. Then, upload the file containing the relevant data by clicking on the small cloud with the arrow inside next to "Uploaded files".
  2. +
  3. Entity identification: For a CSV file, select the connector and CSV mapper to be used by clicking on the icon with an upward arrow in a circle. If it's not a CSV file, the connector will launch automatically. Then, the file import connectors or CSV mappers will identify entities within the uploaded document.
  4. +
  5. Workbench review and validation: Entities identified by connectors are not immediately integrated into the platform's knowledge base. Instead, they are thoughtfully placed in a workbench, awaiting review and validation by an analyst. Workbenches function as draft spaces, ensuring that no data is officially entered into the platform until the workbench has undergone the necessary validation process. For more information on workbenches, refer to the Analyst workbench documentation page.
  6. +
+
+

Review workbenches

+

Import connectors may introduce errors in identifying object types or add "unknown" entities. Workbenches were established with the intent of reviewing the output of connectors before validation. Therefore, it is crucial to be vigilant when examining the workbench to prevent the import of incorrect data into the platform.

+
+

Global import panel

+

Additional information

+

No workbench for CSV mapper

+

It's essential to note that CSV mappers operate differently from other import mechanisms. Unlike connectors, CSV mappers do not generate workbenches. Instead, the data identified by CSV mappers is imported directly into the platform without an intermediary workbench stage.

+

Relationship handling from entity's "Data" tab

+

When importing a document directly from an entity's "Data" tab, there can be an automatic addition of relationships between the objects identified by connectors and the entity in focus. The process differs depending on the type of entity in which the import occurs:

+
    +
  • If the entity is a container (e.g., Report, Grouping, and Cases), the identified objects in the imported file will be linked to the entity (upon workbench validation). In the context of a container, the object is said to be "contained".
  • +
  • For entities that are not containers, a distinct behavior unfolds. In this scenario, the identified objects are not linked to the entity, except for Observables. Related to relationships between the Observables and the entity are automatically added to the workbench and created after validation of this one.
  • +
+

File import in Content tab

+

Expanding the scope of file imports, users can seamlessly add files in the Content tab of Analyses or Cases. In this scenario, the file is directly added as an attachment without utilizing an import mechanism.

+

User capability requirement

+

In order to initiate file imports, users must possess the requisite capability: "Upload knowledge files." This capability ensures that only authorized users can contribute and manage knowledge files within the OpenCTI platform, maintaining a controlled and secure environment for data uploads.

+
+

Deprecation warning

+

Using the ImportDocument connector to parse CSV file is now disallowed as it produces inconsistent results. +Please configure and use CSV mappers dedicated to your specific CSV content for a reliable parsing. +CSV mappers can be created and configured in the administration interface.

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/indicators-lifecycle/index.html b/6.2.X/usage/indicators-lifecycle/index.html new file mode 100755 index 00000000..7b102326 --- /dev/null +++ b/6.2.X/usage/indicators-lifecycle/index.html @@ -0,0 +1,5103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Indicators lifecycle - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Indicators Lifecycle Management

+

Introduction

+

OpenCTI enforces strict rules to determine the period during which an indicator is effective for usage. This period is defined by the valid_from and valid_until dates. All along its lifecycle, the indicator score will decrease according to configured decay rules. After the indicator expires, the object is marked as revoked and the detection field is automatically set to false. Here, we outline how these dates are calculated within the OpenCTI platform and how the score is updated with decay rules.

+

Setting validity dates

+

Data source provided the dates

+

If a data source provides valid_from and valid_until dates when creating an indicator on the platform, these dates are used without modification. But, if the creation is performed from the UI and the indicator is elligible to be manages by a decay rule, the platform will change this valid_until with the one calculated by the Decay rule.

+

Fallback rules for unspecified dates

+

If a data source does not provide validity dates, OpenCTI applies the decay rule matching the indicator to determine these dates. +The valid_until date is computed based on the revoke score of the decay rule : it is set at the exact time at which the indicator will reach the revoke score. +Past valid_until date, the indicator is marked as revoked.

+

Score decay

+

Indicators have an initial score at creation, either provided by data source, or 50 by default. +Over time, this score is going to decrease according to the configured decay rules. +Score is updated at each reaction point defined for the decay rule matching the indicator at creation.

+

Example

+

This URL indicator has matched the Built-in IP and URL decay rule. Its initial score at creation is 100.

+

Indicator overview

+

Right next to the indicator score, there is a button Lifecycle which enables to open a dialog to see the details of the indicator's lifecyle.

+

Indicator lifecycle

+

Conclusion

+

Understanding how OpenCTI calculates validity periods and scores is essential for effective threat intelligence analysis. These rules ensure that your indicators are accurate and up-to-date, providing a reliable foundation for threat intelligence data.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/inferences/index.html b/6.2.X/usage/inferences/index.html new file mode 100755 index 00000000..67b6ad81 --- /dev/null +++ b/6.2.X/usage/inferences/index.html @@ -0,0 +1,5075 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inferences and reasoning - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Inferences and reasoning

+

Overview

+

OpenCTI’s inferences and reasoning capability is a robust engine that automates the process of relationship creation within your threat intelligence data. This capability, situated at the core of OpenCTI, allows logical rules to be applied to existing relationships, resulting in the automatic generation of new, pertinent connections.

+

Understanding inferences and reasoning

+

Inferences and reasoning serve as OpenCTI’s intelligent engine. It interprets your data logically. By activating specific predefined rules (of which there are around twenty), OpenCTI can deduce new relationships from the existing ones. For instance, if there's a connection indicating an Intrusion Set targets a specific country, and another relationship stating that this country is part of a larger region, OpenCTI can automatically infer that the Intrusion Set also targets the broader region.

+

Key benefits

+
    +
  • Efficiency: Reduces manual workload by automating relationship creation, saving valuable analyst time.
  • +
  • Completeness: Fills relationship gaps, ensuring a comprehensive and interconnected threat intelligence database.
  • +
  • Accuracy: Minimizes manual input errors by deriving relationships from predefined, accurate logic.
  • +
+

How it operates

+

When you activate an inference rule, OpenCTI continuously analyzes your existing relationships and applies the defined logical rules. These rules are logical statements that define conditions for new relationships. When the set of conditions is met, the OpenCTI creates the corresponding relationship automatically.

+

For example, if you activate a rule as follows:

+

IF [Entity A targets Identity B] AND [Identity B is part of Identity C] +THEN [Entity A targets Identity C]

+

OpenCTI will apply this rule to existing data. If it finds an Intrusion Set ("Entity A") targeting a specific country ("Identity B") and that country is part of a larger region ("Identity C"), the platform will automatically establish a relationship between the Intrusion Set and the region.

+

Identifying inferred relationships

+

In the knowledge graphs: Inferred relationships are represented by dotted lines of a different color, distinguishing them from non-inferred relations.

+

Inferred_relationships_in_graph

+

In the lists: In a relationship list, a magic wand icon at the end of the line indicates relationship created by inference.

+

Inferred_relationships_in_list

+

Additional resources

+
    +
  • Administration: To find out about existing inference rules and enable/disable them, refer to the Rules engine page in the Administration section of the documentation.
  • +
  • Playbooks: OpenCTI playbooks are highly customizable automation scenarios. This seamless integration allows for further automation, making your threat intelligence processes even more efficient and tailored to your specific needs. More information in our blog post.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/manual-creation/index.html b/6.2.X/usage/manual-creation/index.html new file mode 100755 index 00000000..56299cca --- /dev/null +++ b/6.2.X/usage/manual-creation/index.html @@ -0,0 +1,5092 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Manual creations - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Manual creations

+

Manual data creation in OpenCTI is an intuitive process that occurs throughout the platform. This page provides guidance on two key aspects of manual creation: Entity creation and Relationship creation.

+

Entity creation

+

To create an entity:

+
    +
  1. Navigate to the relevant section: Be on the section of the platform related to the object type you want to create.
  2. +
  3. Click on the "+" icon: Locate the "+" icon located at the bottom right of the window. New report
  4. +
  5. Fill in entity-specific fields: A form on the right side of the window will appear, allowing to fill in specific fields of the entity. Certain fields are inherently obligatory, and administrators have the option to designate additional mandatory fields (See here for more information).
  6. +
  7. Click on "Create": Once you've filled in the desired fields, click on "create" to initiate the entity creation process.
  8. +
+

Create report

+

Relationship creation

+

Before delving into the creation of relationships between objects in OpenCTI, it's crucial to grasp some foundational concepts. Here are key points to understand:

+
    +
  • On several aspects, including relationships, two categories of objects must be differentiated: containers (e.g., Reports, Groupings, and Cases) and others. Containers aren't related to but contains objects.
  • +
  • Relationships, like all other entities, are objects. They possess fields, can be linked, and share characteristics identical to other entities.
  • +
  • Relationships are inherently directional, comprising a "from" entity and a "to" entity. Understanding this directionality is essential for accurate relationship creation.
  • +
  • OpenCTI supports various relationship types, and their usage depends on the entity types being linked. For example, a "target" relationship might link malware to an organization, while linking malware to an intrusion set might involve a different relationship type.
  • +
+

Now, let’s explore the process of creating relationships. To do this, we will differentiate the case of containers from the others.

+

For container

+

When it comes to creating relationships within containers in OpenCTI, the process is straightforward. Follow these steps to attach objects to a container:

+
    +
  1. Navigate to the container: Go to the specific container to which you want to attach an object. This could be a Report, Grouping, or Cases.
  2. +
  3. Access the "Entities" tab: Within the container, locate and access the "Entities" tab.
  4. +
  5. Click on the "+" icon: Find the "+" icon located at the bottom right of the window. New relation with container
  6. +
  7. Search for entities: A side window will appear. Search for the entities you want to add to the container.
  8. +
  9. Add entities to the container: Click on the desired entities. They will be added directly to the container.
  10. +
+

For other

+

When creating relationships not involving a container, the creation method is distinct. Follow these steps to create relationships between entities:

+
    +
  1. Navigate to one of the entities: Go to one of the entities you wish to link. Please be aware that the entity from which you create the relationship will be designated as the "from" entity for that relationship. So the decision of which entity to choose for creating the relationship should be considered, as it will impact the outcome.
  2. +
  3. Access the "Knowledge" tab: Within the entity, go to the "Knowledge" tab.
  4. +
  5. Select the relevant categories: In the right banner, navigate to the categories that correspond to the object to be linked. The available categories depend on the type of entity you are currently on. For example, if you are on malware and want to link to a sector, choose "victimology."
  6. +
  7. Click on the "+" icon: Find the "+" icon located at the bottom right of the window. New relationhip
  8. +
  9. Search for entities: A side window will appear. Search for the entities you want to link.
  10. +
  11. Add entities and click on "Continue": Click on the entities you wish to link. Multiple entities can be selected. Then click on "Continue" at the bottom right. Entities to link
  12. +
  13. Fill in the relationship form: As relationships are objects, a creation form similar to creating an entity will appear.
  14. +
  15. Click on "Create": Once you've filled in the desired fields, click on "create" to initiate the relationship creation process.
  16. +
+

Create relationship

+

Additional methods

+

While the aforementioned methods are primary for creating entities and relationships, OpenCTI offers versatility, allowing users to create objects in various locations within the platform. Here's a non-exhaustive list of additional places that facilitate on-the-fly creation:

+
    +
  • Creating entities during relationship creation: During the "Search for entities" phase (see above) of the relationship creation process, click on the "+" icon to create a new entity directly.
  • +
  • Knowledge graph: Within the knowledge graph - found in the knowledge tab of the containers or in the investigation functionality - users can seamlessly create entities or relationships.
  • +
  • Inside a workbench: The workbench serves as another interactive space where users can create entities and relationships efficiently.
  • +
+

These supplementary methods offer users flexibility and convenience, allowing them to adapt their workflow to various contexts within the OpenCTI platform. As users explore the platform, they will naturally discover additional means of creating entities and relationships.

+
+

Max confidence level

+
+

When creating knowledge in the platform, the maximum confidence level of the users is used. Please navigate to this page to understand this concept and the impact it can have on the knowledge creation.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/merging/index.html b/6.2.X/usage/merging/index.html new file mode 100755 index 00000000..222c37fd --- /dev/null +++ b/6.2.X/usage/merging/index.html @@ -0,0 +1,5036 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Merge objects - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+ +
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Merge objects

+

Introduction

+

OpenCTI’s merge capability stands as a pivotal tool for optimizing threat intelligence data, allowing to consolidate multiple entities of the same type. This mechanism serves as a powerful cleanup tool, harmonizing the platform and unifying scattered information. In this section, we explore the significance of this feature, the process of merging entities, and the strategic considerations involved.

+

+

Data streamlining

+

In the ever-expanding landscape of threat intelligence and the multitude of names chosen by different data sources, data cleanliness is essential. Duplicates and fragmented information hinder efficient analysis. The merge capability is a strategic solution for amalgamating related entities into a cohesive unit. Central to the merging process is the selection of a main entity. This primary entity becomes the anchor, retaining crucial attributes such as name and description. Other entities, while losing specific fields like descriptions, are aliased under the primary entity. This strategic decision preserves vital data while eliminating redundancy.

+

Preserving entity relationships

+

One of the key feature of the merge capability is its ability to preserve relationships. While merging entities, their interconnected relationships are not lost. Instead, they seamlessly integrate into the new, merged entity. This ensures that the intricate web of relationships within the data remains intact, fostering a comprehensive understanding of the threat landscape.

+

Conclusion

+

OpenCTI’s merge capability helps improve the quality of threat intelligence data. By consolidating entities and centralizing relationships, OpenCTI empowers analysts to focus on insights and strategies, unburdened by data silos or fragmentation. However, exercising caution and foresight in the merging process is essential, ensuring a robust and streamlined knowledge basis.

+

Additional resources

+
    +
  • Administration: To understand how to merge entities and the consideration to take into account, refer to the Merging page in the Administration section of the documentation.
  • +
  • Deduplication mechanism: the platform is equipped with deduplication processes that automatically merge data at creation (either manually or by importing data from different sources) if it meets certain conditions.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/nested/index.html b/6.2.X/usage/nested/index.html new file mode 100755 index 00000000..c94ef28d --- /dev/null +++ b/6.2.X/usage/nested/index.html @@ -0,0 +1,5137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Nested objects - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Nested references and objects

+

STIX standard

+

Definition

+

In the STIX 2.1 standard, objects can:

+
    +
  1. Refer to other objects in directly in their attributes, by referencing one or multiple IDs.
  2. +
  3. Have other objects directly embedded in the entity.
  4. +
+

Example

+
{
+   "type": "intrusion-set",
+   "spec_version": "2.1",
+   "id": "intrusion-set--4e78f46f-a023-4e5f-bc24-71b3ca22ec29",
+   "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", // nested reference to an identity
+   "object_marking_refs": ["marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"], // nested reference to multiple marking defintions
+   "external_references": [
+      {
+         "source_name": "veris",
+         "external_id": "0001AA7F-C601-424A-B2B8-BE6C9F5164E7",
+         "url": "https://github.com/vz-risk/VCDB/blob/125307638178efddd3ecfe2c267ea434667a4eea/data/json/validated/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json",    
+      }
+   ],
+   "created": "2016-04-06T20:03:48.000Z",
+   "modified": "2016-04-06T20:03:48.000Z",
+   "name": "Bobcat Breakin",
+   "description": "Incidents usually feature a shared TTP of a bobcat being released within the building containing network access...",
+   "aliases": ["Zookeeper"],
+   "goals": ["acquisition-theft", "harassment", "damage"]
+}
+
+

In the previous example, we have 2 nested references to other objects in:

+
"created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", // nested reference to an identity
+"object_marking_refs": ["marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"], // nested reference to multiple marking defintions
+
+

But we also have a nested object within the entity (an External Reference):

+
"external_references": [
+   {
+      "source_name": "veris",
+      "external_id": "0001AA7F-C601-424A-B2B8-BE6C9F5164E7",
+      "url": "https://github.com/vz-risk/VCDB/blob/125307638178efddd3ecfe2c267ea434667a4eea/data/json/validated/0001AA7F-C601-424A-B2B8-BE6C9F5164E7.json",    
+   }
+]
+
+

Implementation

+

Modelization

+

In OpenCTI, all nested references and objects are modelized as relationships, to be able to pivot more easily on labels, external references, kill chain phases, marking definitions, etc.

+

Investigation

+

Import & export

+

When importing and exporting data to/from OpenCTI, the translation between nested references and objects to full-fledged nodes and edges is automated and therefore transparent for the users. Here is an example with the object in the graph above:

+
{
+   "id": "file--b6be3f04-e50f-5220-af3a-86c2ca66b719",
+   "spec_version": "2.1",
+   "x_opencti_description": "...",
+   "x_opencti_score": 50,
+   "hashes": {
+       "MD5": "b502233b34256285140676109dcadde7"
+   },
+   "labels": [
+       "cookiecutter",
+       "clouddata-networks-1"
+   ],
+   "external_references": [
+       {
+           "source_name": "Sekoia.io",
+           "url": "https://app.sekoia.io/intelligence/objects/indicator--3e6d61b4-d5f0-48e0-b934-fdbe0d87ab0c"
+       }
+   ],
+   "x_opencti_id": "8a3d108f-908c-4833-8ff4-4d6fc996ce39",
+   "type": "file",
+   "created_by_ref": "identity--b5b8f9fc-d8bf-5f85-974e-66a7d6f8d4cb",
+   "object_marking_refs": [
+       "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
+   ]
+}
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/notifications/index.html b/6.2.X/usage/notifications/index.html new file mode 100755 index 00000000..685bbe06 --- /dev/null +++ b/6.2.X/usage/notifications/index.html @@ -0,0 +1,5321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notifications and alerting - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Notifications and alerting

+

In the evolving landscape of cybersecurity, timely awareness is crucial. OpenCTI empowers users to stay informed and act swiftly through its robust notifications and alerting system. This feature allows users to create personalized triggers that actively monitor the platform for specific events and notify them promptly when these conditions are met.

+

From individual users tailoring their alert preferences to administrators orchestrating collaborative triggers for Groups or Organizations, OpenCTI's notification system is a versatile tool for keeping cybersecurity stakeholders in the loop.

+

The main menu "Notifications and triggers" for creating and managing notifications is located in the top right-hand corner with the bell icon.

+

Notifications

+

Triggers

+

In OpenCTI, triggers serve as personalized mechanisms for users to stay informed about specific events that align with their cybersecurity priorities. Users can create and manage triggers to tailor their notification experience. Each trigger operates by actively listening to events based on predefined filters and event types, promptly notifying users via chosen notifiers when conditions are met.

+

Triggers

+

Trigger management

+

Individual user triggers: Each user possesses the autonomy to craft their own triggers, finely tuned to their unique preferences and responsibilities. By setting up personalized filters and selecting preferred notifiers, users ensure that they receive timely and relevant notifications aligned with their specific focus areas.

+

Administrative control: Platform administrators have the capability to create and manage triggers for Users, Groups and Organizations. This provides centralized control and the ability to configure triggers that address collective cybersecurity objectives. Users within the designated Group or Organization will benefit from triggers with read-only access rights. These triggers are to be created directly on the User|Group|Organization with whom to share them in "Settings > Security > Users|Groups|Organizations".

+

Restricted trigger

+

Trigger filters

+

Leveraging the filters, users can meticulously define the criteria that activate their triggers. This level of granularity ensures that triggers are accurate, responding precisely to events that matter most. Users can tailor filters to consider various parameters such as object types, markings, sources, or other contextual details. They can also allow notifications for the assignment of a Task, a Case, etc.

+

Beyond filters, a trigger can be configured to respond to three event types: creation, modification, and deletion.

+

Trigger configuration

+

Instance triggers

+

Instance triggers offer a targeted approach to live monitoring by allowing users to set up triggers specific to one or several entities. These triggers, when activated, keep a vigilant eye on a predefined set of events related to the selected entities, ensuring that you stay instantly informed about crucial changes.

+

Creating instance triggers

+

Method 1: Using the general trigger creation form

+
    +
  1. Go on the "Notifications and triggers" window.
  2. +
  3. Navigate to the "Triggers and digests" tab.
  4. +
  5. Access the general trigger creation form.
  6. +
  7. Toggle the switch "Instance trigger".
  8. +
  9. Choose the entities to monitor.
  10. +
+

Instance trigger creation

+

Method 2: Quick subscription

+
    +
  1. On an entity's overview, locate the "Instance trigger quick subscription" button with the bell icon at the top right.
  2. +
  3. Click on the button to create the instance trigger.
  4. +
  5. (Optional) Click on it again to modify the instance trigger created.
  6. +
+

Quick subscription button

+

Events monitored by instance triggers

+

An instance trigger set on an entity X actively monitors the following events:

+
    +
  • Update/Deletion of X: Stay informed when the selected entity undergoes changes or is deleted.
  • +
  • Creation/Deletion of relationships: Receive notifications about relationships being added or removed from/to X.
  • +
  • Creation/Deletion of related entities: Be alerted when entities that have X in its refs - i.e. contains X, is shared with X, is created by X, etc. - are created or deleted.
  • +
  • Adding/Removing X in ref: Stay in the loop when X is included or excluded from the ref of other entities - i.e. adding X in the author of an entity, adding X in a report, etc.).
  • +
+
+

Entity deletion notification

+

It's important to note that the notification of entity deletion can occur in two scenarios: + - Real entity deletion: When the entity is genuinely deleted from the platform. + - Visibility loss: When a modification to the entity results in the user losing visibility for that entity.

+
+

Digest

+

Digests provide an efficient way to streamline and organize your notifications. By grouping notifications based on selected triggers and specifying the delivery period (daily, weekly, monthly), you gain the flexibility to receive consolidated updates at your preferred time, as opposed to immediate notifications triggered by individual events.

+

Creating digests

+
    +
  1. Go on the "Notifications and triggers" window.
  2. +
  3. Navigate to the "Triggers and digests" tab.
  4. +
  5. Create a new digest.
  6. +
  7. Configure digest: Set the parameters, including triggers to be included and the frequency of notifications (daily, weekly, monthly).
  8. +
  9. Choose the notifier(s): Select the notification method(s) (e.g. within the OpenCTI interface, via email, etc.).
  10. +
+

Digest configuration

+

Benefits of digests

+
    +
  • Organized notifications: Digests enable you to organize and categorize notifications, preventing a flood of individual alerts.
  • +
  • Customized delivery: Choose the frequency of digest delivery based on your preferences, whether it's a daily overview, a weekly summary, or a monthly roundup.
  • +
  • Reduced distractions: Receive notifications at a scheduled time, minimizing interruptions and allowing you to focus on critical tasks.
  • +
+

Digests enhance your control over notification management, ensuring a more structured and convenient approach to staying informed about important events.

+

Notifiers

+

In OpenCTI, notifiers serve as the channels for delivering notifications, allowing users to stay informed about critical events. The platform offers two built-in notifiers, "Default mailer" for email notifications and "User interface" for in-platform alerts.

+

Notifiers

+

Notifier connectors

+

OpenCTI features built-in notifier connectors that empower users to create personalized notifiers for notification and activity alerting. Three essential connectors are available:

+
    +
  • Platform mailer connector: Enables sending notifications directly within the OpenCTI platform.
  • +
  • Simple mailer connector: Offers a straightforward approach to email notifications with simplified configuration options.
  • +
  • Generic webhook connector: Facilitates communication through webhooks.
  • +
+

OpenCTI provides two samples of webhook notifiers designed for Teams integration.

+

Custom notifiers

+

Configuration and Access

+

Notifiers are manageable in the "Settings > Customization > Notifiers" window and can be restricted through Role-Based Access Control (RBAC). Administrators can restrict access to specific Users, Groups, or Organizations, ensuring controlled usage.

+

For guidance on configuring custom notifiers and explore detailed setup instructions, refer to the dedicated documentation page.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/overview/index.html b/6.2.X/usage/overview/index.html new file mode 100755 index 00000000..934e6c08 --- /dev/null +++ b/6.2.X/usage/overview/index.html @@ -0,0 +1,5435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Overview

+

Introduction

+

The following chapter aims at giving the reader a step-by-step description of what is available on the platform and the meaning of the different tabs and entries.

+

When the user connects to the platform, the home page is the Dashboard. This Dashboard contains several visuals summarizing the types and quantity of data recently imported into the platform.

+
+

Dashboard

+

To get more information about the components of the default dashboard, you can consult the Getting started.

+
+

The left side panel allows the user to navigate through different windows and access different views and categories of knowledge.

+
+

Menu

+
+

Structure

+

The "hot knowledge"

+

The first part of the platform in the left menu is dedicated to what we call the "hot knowledge", which means this is the entities and relationships which are added on a daily basis in the platform and which generally require work / analysis from the users.

+
    +
  • Analyses: all containers which convey relevant knowledge such as reports, groupings and malware analyses.
  • +
  • Cases: all types of case like incident responses, requests for information, for takedown, etc.
  • +
  • Events: all incidents & alerts coming from operational systems as well as sightings.
  • +
  • Observations: all technical data in the platform such as observables, artifacts and indicators.
  • +
+

The "cold knowledge"

+

The second part of the platform in the left menu is dedicated to the "cold knowledge", which means this is the entities and relationships used in the hot knowledge. You can see this as the "encyclopedia" of all pieces of knowledge you need to get context: threats, countries, sectors, etc.

+
    +
  • Threats: all threats entities from campaigns to threat actors, including intrusion sets.
  • +
  • Arsenal: all tools and pieces of malware used and/or targeted by threats, including vulnerabilities.
  • +
  • Techniques: all objects related to tactics and techniques used by threats (TTPs, etc.).
  • +
  • Entities: all non-geographical contextual information such as sectors, events, organizations, etc.
  • +
  • Locations: all geographical contextual information, from cities to regions, including precise positions.
  • +
+

Hide categories

+

You can customize the experience in the platform by hiding some categories in the left menu, whether globally or for a specific role.

+

Hide categories globally

+

In the Settings > Parameters, it is possible for the platform administrator to hide categories in the platform for all users.

+

Hide categories globally

+

Hide categories in roles

+

In OpenCTI, the different roles are highly customizable. It is possible to defined default dashboards, triggers, etc. but also be able to hide categories in the roles:

+

Hide categories in roles

+

Presentation of a typical page in OpenCTI

+

While OpenCTI features numerous entities and tabs, many of them share similarities, with only minor differences arising from specific characteristics. These differences may involve the inclusion or exclusion of certain fields, depending on the nature of the entity.

+

In this part will only be detailed a general outline of a "typical" OpenCTI page. The specifies of the different entities will be detailed in the corresponding pages below (Activities and Knowledge).

+
+

Top menu of an entity

+
+

+

Overview

+

In the Overview tab on the entity, you will find all properties of the entity as well as the recent activities.

+

First, you will find the Details section, where are displayed all properties specific to the type of entity you are looking at, an example below with a piece of malware:

+

Details

+

Thus, in the Basic information section, are displayed all common properties to all objects in OpenCTI, such as the marking definition, the author, the labels (i.e. tags), etc.

+

Basic information

+

Below these two sections, you will find latest modifications in the Knowledge base related to the Entity:

+
    +
  • Latest created relationships: display the latest relationships that have been created from or to this Entity. For example, latest Indicators of Compromise and associated Threat Actor of a Malware.
  • +
  • latest containers about the object: display all the Cases and Analyses that contains this Entity. For example, the latest Reports about a Malware.
  • +
  • External references: display all the external sources associated with the Entity. You will often find here links to external reports or webpages from where Entity's information came from.
  • +
  • History: display the latest chronological modifications of the Entity and its relationships that occurred in the platform, in order to traceback any alteration.
  • +
+

Latest relations and containers +References and History

+

Last, all Notes written by users of the platform about this Entity are displayed in order to access unstructured analysis comments.

+

+

Knowledge

+

In the Knowledge tab, which is the central part of the entity, you will find all the Knowledge related to the current entity. The Knowledge tab is different for Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) entities than for all the other entity types.

+
    +
  • The Knowledge tab of those entities (who represents Analyses or Cases that can contains a collection of Objects) is the place to integrate and link together entities. For more information on how to integrate information in OpenCTI using the knowledge tab of a report, please refer to the part Manual creation.
  • +
  • The Knowledge tabs of any other entity (that does not aim to contain a collection of Objects) gather all the entities which have been at some point linked to the entity the user is looking at. For instance, as shown in the following capture, the Knowledge tab of Intrusion set APT29, gives access to the list of all entities APT29 is attributed to, all victims the intrusion set has targeted, all its campaigns, TTPs, malware etc. For entities to appear in these tabs under Knowledge, they need to have been linked to the entity directly or have been computed with the inference engine.
  • +
+

The Intrusion Set's Knowledge tab

+

Focus on Indicators and Observables

+

The Indicators and Observables section offers 3 display modes: +- The entities view, which displays the indicators/observables linked to the entity. +- The relationship view, which displays the various relationships between the indicators/observables linked to the entity and the entity itself. +- The contextual view, which displays the indicators/observables contained in the cases and analyses that contain the entity.

+

The Knowledge Focus on Indicators and Observables views

+

+

Content

+

The Content tab allows for uploading and creating outcomes documents related to the content of the current entity (in PDF, text, HTML or markdown files). This specific tab enable to previzualize, manage and write deliverable associated with the entity. For example an analytic report to share with other teams, a markdown files to feed a collaborative wiki with, etc.

+

The Content tab is available for a subset of entities: Report, Incident, Incident response, Request for Information, and Request for Takedown.

+

The Content tab of an Incident

+

+

Analyses

+

The Analyses tab contains the list of all Analyses (Report, Groupings) and Cases (Incident response, Request for Information, Request for Takedown) in which the entity has been identified.

+

Example of the list of reports in which the attack pattern "data obfuscation" has been identified.

+

By default, this tab display the list, but you can also display the content of all the listed Analyses on a graph, allowing you to explore all their Knowledge and have a glance of the context around the Entity.

+

Graph view of all the Knowledge contained in the listed analyses

+

+

Data

+

The Data tab contains documents that are associated to the object and were either:

+
    +
  • Uploaded to the platform : for instance the PDF document containing the text of the report
  • +
  • Generated from the platform to be downloaded : a JSON or CSV file containing information on the object and generated by the user.
  • +
  • associated to an external reference
  • +
+

Analyst Workbench can also be created from here. They will contain the entity by default.

+

The Data tab of a Threat Actor

+

In addition, the Data tab of Threat actors (group), Threat actors (individual), Intrusions sets, Organizations, Individuals have an extra panel:

+
    +
  • Pictures Management: allows to manage the profile pictures attached to the entity.
  • +
+

Profile pictures panel +Profile pictures of a Threat Actor

+

+

History

+

The History tab displays the history of change of the Entity, update of attributes, creation of relations, ...

+

Because of the volumes of information the history is written in a specific index that consume the redis stream to rebuild the history for the UI. +History tab of a Malware

+

Less frequent tabs

+
    +
  • The Observables tab (for Reports and Observed data): A table containing all SCO (Stix Cyber Observable) contained in the Report or the Observed data, with search and filters available. It also displays if the SCO has been added directly or through inferences with the reasoning engine
  • +
  • the Entities tab (for Reports and Observed data): A table containing all SDO (Stix Domain Objects) contained in the Report or the Observed data, with search and filters available. It also displays if the SDO has been added directly or through inferences with the reasoning engine
  • +
  • Observables:
  • +
  • the Sightings tab (for Indicators and Observables): A table containing all Sightings relationships corresponding to events in which Indicators (IP, domain name, URL, etc.) are detected by or within an information system, an individual or an organization. Most often, this corresponds to a security event transmitted by a SIEM or EDR.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/pivoting/index.html b/6.2.X/usage/pivoting/index.html new file mode 100755 index 00000000..a597684f --- /dev/null +++ b/6.2.X/usage/pivoting/index.html @@ -0,0 +1,5182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pivot and investigate - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Pivot and investigate

+

In OpenCTI, all data are structured as an extensive knowledge graph, where every element is interconnected. The investigation functionality provides a powerful tool for pivoting on any entity or relationship within the platform. Pivoting enables users to explore and analyze connections between entities and relationships, facilitating a comprehensive understanding of the data.

+

To access investigations, navigate to the top right corner of the toolbar:

+

Top menu investigation

+
+

Access restriction

+

When an investigation is created, it is initially visible only to the creator, allowing them to work on the investigation before deciding to share it. The sharing mechanism is akin to that of dashboards. For further details, refer to the Access control section in the dashboard documentation page.

+
+

Perform investigation

+

Manipulate entity

+

We can add any existing entity of the platform to your investigation.

+

Investigation bottom right menu

+

After adding an entity, we can choose the entity and view its details in the panel that appears on the right of the screen.

+

On each node, we'll notice a bullet with a number inside, serving as a visual indication of how many entities are linked to it but not currently displayed in the graph. Keep in mind that this number is an approximation, which is why there's a "+" next to it. If there's no bullet displayed, it means there's nothing to expand from this node.

+

Investigation workspace

+

Expansion

+

To incorporate these linked entities into the graph, we just have to expand the nodes. Utilize the button with a 4-arrows logo in the mentioned menu, or double-click on the entity directly. This action opens a new window where we can choose the types of entities and relationships we wish to expand.

+

Investigation expand entity

+

For instance, in the image above, selecting the target Malware and the relationship Uses implies expanding in my investigation graph all Malware linked to this node with a relationship of type Uses.

+

Roll back expansion

+

Expanding a graph can add a lot of entities and relations, making it not only difficult to read but sometimes counterproductive since it brings entities and relations that are not useful to your investigations. +To solve this problem, there is a button to undo the last expansion.

+

When clicking on this button, we will retrieve the state in which your graph was before your expansion. As a result, please note that all add or remove actions made since the last expansion will be lost: in other words, if you have expanded your graph, and then have added some entities in your graph, when clicking on rollback button, the entities that you have added will not be in your graph.

+

Investigation rollback popup

+

You can roll back your investigation graph up to the last 10 expand actions.

+

Manipulate relationship

+

We can create a relationship between entities directly within our investigation. To achieve this, select multiple entities by clicking on them while holding down the shift key. Subsequently, a button appears at the bottom right to create one (or more, depending on the number of entities selected) relationships.

+

Investigation create relationship

+
+

Relationship creation

+

Creating a relationship in the investigation graph will generate the relationship in your knowledge base.

+
+

Capitalize on an investigation

+

Export investigation

+

Users have the capability to export investigations, providing a way to share, document, or archive their findings.

+
    +
  • PDF and image formats: Users can export investigations in either PDF or image format, offering flexibility in sharing and documentation.
  • +
  • STIX bundle: The platform allows the export of the entire content of an investigation graph as a STIX bundle. In the STIX format, all objects within the investigation graph are automatically aggregated into a Report object.
  • +
+

Investigation export

+

Turn investigation into a container

+

Users can efficiently collect and consolidate the findings of an investigation by adding the content into dedicated containers. The contents of an investigation can be imported into various types of containers, including:

+
    +
  • Grouping
  • +
  • Incident Response
  • +
  • Report
  • +
  • Request for Information
  • +
  • Request for Takedown
  • +
+

investigation-turn-to-report-or-case.png

+

We have the flexibility to choose between creating a new container on the fly or adding investigation content to an existing container.

+

investigation-turn-to-report-or-case-dialog-new-entity.png

+

investigation-turn-to-report-or-case-dialog-new-entity-form.png

+

After clicking on the ADD button, the browser will redirect to the Knowledge tab of the container where we added the content of our investigation. If we added it to multiple containers, the redirection will be to the first of the list.

+

investigation-turn-to-report-or-case-success.png

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/reliability-confidence/index.html b/6.2.X/usage/reliability-confidence/index.html new file mode 100755 index 00000000..6a35d549 --- /dev/null +++ b/6.2.X/usage/reliability-confidence/index.html @@ -0,0 +1,5433 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reliability and confidence - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Reliability and Confidence

+

Generalities

+

In (Cyber) Threat Intelligence, evaluation of information sources and of information quality is one of the most important aspect of the work. It is of the utter most importance to assess situations by taking into account reliability of the sources and credibility of the information.

+

This concept is foundational in OpenCTI, and have real impact on:

+ +

What is the Reliability of a source?

+

Reliability of a source of information is a measurement of the trust that the analyst can have about the source, based on the technical capabilities or history of the source. Is the source a reliable partner with long sharing history? A competitor? Unknown?

+

Reliability of sources are often stated at organizational level, as it requires an overview of the whole history with it.

+

In the Intelligence field, Reliability is often notated with the NATO Admiralty code.

+

What is Confidence of an information?

+

Reliability of a source is important but even a trusted source can be wrong. Information in itself has a credibility, based on what is known about the subject and the level of corroboration by other sources.

+

Credibility is often stated at the analyst team level, expert of the subject, able to judge the information with its context.

+

In the Intelligence field, Confidence is often notated with the NATO Admiralty code.

+
+

Why Confidence instead of Credibility?

+

Using both Reliability and Credibility is an advanced use case for most of CTI teams. It requires a mature organization and a well staffed team. For most of internal CTI team, a simple confidence level is enough to forge assessment, in particular for teams that concentrate on technical CTI.

+

Thus in OpenCTI, we have made the choice to fuse the notion of Credibility with the Confidence level that is commonly used by the majority of users. They have now the liberty to push forward their practice and use both Confidence and Reliability in their daily assessments.

+
+

Reliability open vocabulary

+

Reliability value can be set for every Entity in the platform that can be Author of Knowledge:

+
    +
  • Organizations
  • +
  • Individuals
  • +
  • Systems
  • +
  • and also Reports
  • +
+

Reliability on Reports allows you to specify the reliability associated to the original author of the report if you received it through a provider.

+

For all Knowledge in the platform, the reliability of the source of the Knowledge (author) is displayed in the Overview. This way, you can always forge your assessment of the provided Knowledge regarding the reliability of the author.

+

Entity reliability and confidence

+

You can also now filter entities by the reliability of its author.

+
+

Tip

+

This way, you may choose to feed your work with only Knowledge provided by reliable sources.

+
+

Reliability is an open vocabulary that can be customized in Settings -> Taxonomies -> Vocabularies : reliability_ov.

+
+

Info

+

The setting by default is the Reliability scale from NATO Admiralty code. But you can define whatever best fit your organization.

+
+

Settings reliability

+

Confidence scale

+

Confidence level can be set for:

+
    +
  • Analyses: Report, Grouping, Malware analysis, Notes
  • +
  • Cases: Incident Response, Request for Information, Request for Takedown, Feedback
  • +
  • Events: Incident, Sighting, Observed data
  • +
  • Observations: Indicator, Infrastructure
  • +
  • Threats: Threat actor (Group), Threat actor (Individual), Intrusion Set, Campaign
  • +
  • Arsenal: Malware, Channel, Tool, Vulnerability
  • +
+

For all of these entities, the Confidence level is displayed in the Overview, along with the Reliability. This way, you can rapidly assess the Knowledge with the Confidence level representing the credibility/quality of the information.

+

Confidence scale customization

+

Confidence level is a numerical value between 0 and 100. But Multiple "Ticks" can be defined and labelled to provide a meaningful scale.

+

Confidence level can be customized for each entity type in Settings > Customization > Entity type.

+

Confidence level customization

+

As such customization can be cumbersome, three confidence level templates are provided in OpenCTI:

+
    +
  • Admiralty: corresponding to the Admiralty code's credibility scale
  • +
  • Objective: corresponding to a full objective scale, aiming to leave any subjectivity behind. With this scale, an information confidence is:
      +
    • "Cannot be judged": there is no data regarding the credibility of the information
    • +
    • "Told": the information is known because it has been told to the source. The source doesn't verify it by any means.
    • +
    • "Induced": the information is the result of an analysis work and is based on other similar information assumed to be true.
    • +
    • "Deduced": the information is the result of an analysis work, and is a logical conclusion of other information assumed to be true.
    • +
    • "Witnessed": the source have observed itself the described situation or object.
    • +
    +
  • +
  • standard: the historic confidence level scale in OpenCTI defining a Low, Med and High level of confidence.
  • +
+

It is always possible to modify an existing template to define a custom scale adapted to your context.

+
+

Tip

+

If you use the Admiralty code setting for both reliability and Confidence, you will find yourself with the equivalent of NATO confidence notation in the Overview of your different entities (A1, B2, C3, etc.)

+
+

+

Max confidence level

+

Overview

+

We know that in organizations, different users do not always have the same expertise or seniority. As a result, some specific users can be more "trusted" when creating or updating knowledge than others. Additionally, because connectors, TAXII feeds and streams are all linked to respectively one user, it is important to be able to differentiate which connector, stream or TAXII feed is more trustable than others.

+

This is why we have introduced the concept of max confidence level to tackle this use case.

+

Max confidence level per user allows organizations to fine tune their users to ensure knowledge updated and created stays as consistent as possible.

+

The maximum confidence level can be set at the Group level or at the User level, and can be overridden by entity type for fine-tuning your confidence policy.

+

Overall way of working

+

The overall idea is that users with a max confidence level lower than a confidence level of an entity cannot update or delete this entity.

+

Also, in a conservative approach, when 2 confidence levels are possible, we would always take the lowest one.

+

To have a detailed understanding of the concept, please browse through this diagram:

+ + +

User effective confidence level

+

User and group confidence level configuration shall be viewed as:

+
    +
  • a maximum confidence level between 0 and 100 (optional for users, mandatory for groups);
  • +
  • a list of overrides (a max confidence level between 0 and 100) per entity type (optional).
  • +
+

The user's effective confidence level is the result of this configuration from multiple sources (user and their groups).

+

To compute this value, OpenCTI uses the following strategy:

+
    +
  • effective maximum confidence is the maximum value found in the user's groups;
  • +
  • effective overrides per entity type are cumulated from all groups, taking the maximum value if several overrides are set on the same entity type
  • +
  • if a user maximum confidence level is set, it overrides everything from groups, including the overrides per entity type defined at group level
  • +
  • if not, but the user has specific overrides per entity types, they override the corresponding confidence levels per entity types coming from groups
  • +
  • if a user has the administrator's "Bypass" capability, the effective confidence level will always be 100 without overrides, regardless of the group and user configuration on confidence level
  • +
+

The following diagram describes the different use-cases you can address with this system.

+ + +

How to set a confidence level

+

You can set up a maximum confidence levels from the Confidences tab in the edition panel of your user or group. +The value can be selected between 0 and 100, or using the admiralty scale selector.

+

At the group level, the maximum confidence level is mandatory, but is optional at the user level (you have to enable it using the corresponding toggle button).

+

Update Confidence Level

+

How to override a max confidence level per entity type

+

You also have the possibility to override a max confidence level per entity type, limited to Stix Domain Objects.

+

Override Max Confidence Level Per Entity

+

You can visualize the user's effective confidence level in the user's details view, by hovering the corresponding tooltip. +It describes where the different values might come from.

+

Override Confidence Tooltip

+

Usage in OpenCTI

+

Example with the admiralty code template

+

Your organization have received a report from a CTI provider. At your organization level, this provider is considered as reliable most of the time and its reliability level has been set to "B - Usually Reliable" (your organization uses the Admiralty code).

+

This report concerns ransomware threat landscape and have been analysed by your CTI analyst specialized in cybercrime. This analyst has granted a confidence level of "2 - Probably True" to the information.

+

As a technical analyst, through the cumulated reliability and Confidence notations, you now know that the technical elements of this report are probably worth consideration.

+

Example with the admiralty code template

+

Example with the Objective template

+

As a CTI analyst in a governmental CSIRT, you build up Knowledge that will be shared within the platform to beneficiaries. Your CSIRT is considered as a reliable source by your beneficiaries, even if you play a role of a proxy with other sources, but your beneficiaries need some insights about how the Knowledge has been built/gathered.

+

For that, you use the "Objective" confidence scale in your platform to provide beneficiaries with that. When the Knowledge is the work of the investigation of your CSIRT, either from incident response or attack infrastructure investigation, you set the confidence level to "Witnessed", "Deduced" or "Induced" (depending on if you observed directly the data, or inferred it during your research). When the information has not been verified by the CSIRT but has value to be shared with beneficiaries, you can use the "Told" level to make it clear to them that the information is probably valuable but has not been verified.

+

Example with the Objective template

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/search/index.html b/6.2.X/usage/search/index.html new file mode 100755 index 00000000..b2a14d67 --- /dev/null +++ b/6.2.X/usage/search/index.html @@ -0,0 +1,5102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Search for knowledge - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Search for knowledge

+

In OpenCTI, you have access to different capabilities to be able to search for knowledge in the platform. In most cases, a search by keyword can be refined with additional filters for instance on the type of object, the author etc.

+ +

The global search is always available in the top bar of the platform.

+
+

Search bar

+
+

This search covers all STIX Domain Objects (SDOs) and STIX Cyber Observables (SCOs) in the platform. The search results are sorted according to the following behaviour:

+
    +
  • Priority 1 for exact matching of the keyword in one attribute of the objects.
  • +
  • Priority 2 for partial matching of the keyword in the name, the aliases and the description attributes (full text search).
  • +
  • Priority 3 for partial matching of the keyword in all other attributes (full text search).
  • +
+

If you get unexpected result, it is always possible to add some filters after the initial search:

+

Search filters

+

Also, using the Advanced search button, it is possible to directly put filters in a global search:

+

Advanced search

+
+

Advanced filters

+

You have access to advanced filters all accross the UI, if you want to know more about how to use these +filters with the API or the Python library, don't hesitate to read the dedicated page

+
+

Full text search in files content

+
+

Enterprise edition

+

Full text search in files content is available under the "OpenCTI Enterprise Edition" license.

+

Please read the dedicated page to have all information

+
+

It's possible to extend the global search by keywords to the content of documents uploaded to the platform via the Data import tab, or directly linked to an entity via its Data tab.

+

It is particularly useful to enable Full text indexing to avoid missing important information that may not have been structured within the platform. This situation can arise due to a partial automatic import of document content, limitations of a connector, and, of course, errors during manual processing.

+

Files search

+

In order to search in files, you need to configure file indexing.

+ +

The bulk search capabilities is available in the top bar of the platform and allows you to copy paste a list of keyword or objects (ie. list of domains, list of IP addresses, list of vulnerabilities, etc.) to search in the platform:

+

Bulk search

+

When searching in bulk, OpenCTI is only looking for an exact match in some properties:

+
    +
  • name
  • +
  • aliases
  • +
  • x_opencti_aliases
  • +
  • x_mitre_id
  • +
  • value
  • +
  • subject
  • +
  • abstract
  • +
  • hashes.MD5
  • +
  • hashes.SHA-1
  • +
  • hashes.SHA-256
  • +
  • hashes.SHA-512
  • +
  • x_opencti_additional_names
  • +
+

When something is not found, it appears in the list as Unknown and will be excluded if you choose to export your search result in a JSON STIX bundle or in a CSV file.

+

Bulk search results

+ +

In most of the screens of knowledge, you always have a contextual search bar allowing you to filter the list you are on:

+

Contextual search

+

The search keyword used here is taken into account if you decide to export the current view in a file such as a JSON STIX bundle or a CSV file.

+

Other search bars

+

Some other screens can contain search bars for specific purposes. For instance, in the graph views to filter the nodes displayed on the graph:

+

Search in graph

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/tips-widget-creation/index.html b/6.2.X/usage/tips-widget-creation/index.html new file mode 100755 index 00000000..e86ea4c1 --- /dev/null +++ b/6.2.X/usage/tips-widget-creation/index.html @@ -0,0 +1,5264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tips for widget creation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Pro-tips on widget creation

+

Previously, the creation of widgets has been covered. To help users being more confident in creating widgets, here are some details to master the widget creation.

+

+

How to choose the appropriate widget visualization for your use case?

+

We can classify the widgets in 3 different types.

+

Single dimension widgets

+

Use these widgets when you would like to display information about one single type of object (entity or relation).

+
    +
  • Widget visualizations: number, list, list (distribution), timeline, donuts, radar, map, bookmark, tree map.
  • +
  • Use case example: view the amount of malware in platform (number widget), view the top 10 threat actor group target a specific country (distribution list widget), etc.
  • +
+

Multi dimension widgets

+

Use these widgets if you would like to compare or have some insights about similar types of object (up to 5).

+
    +
  • Widget visualizations: line, area, heatmap, vertical bars.
  • +
  • Use case example: view the amount of malware, intrusion sets, threat actor groups added in the course of last month in the platform (line or area widget).
  • +
+
+

Type of object in widget

+
+

These widgets need to use the same "type" of object to work properly. You always need to add relationships in the filter view if you have selected a "knowledge graph" perspective. If you have selected the knowledge graph entity, adding "Entities" (click on + entities) will not work, since you are not counting the same things.

+

Break down widgets

+

Use this widget if you want to divide your data set into smaller parts to make it clearer and more useful for analysis.

+
    +
  • Widget visualization: horizontal bars.
  • +
  • Use case example: view the list of malware targeting a country breakdown by the type of malware.
  • +
+

breakdown example

+

Adding datasets to your widget

+

Adding datasets can serve two purposes: comparing data or breakdown a view to have deeper understanding on what a specific dataset is composed of.

+

Use Case 1: compare several datasets

+

As mentioned in How to choose the appropriate widget visualization for your use case? section you can add data sets to compare different data. Make sure to add the same type of objects (entities or relations) to be able to compare the same objects, by using access buttons like +, + Relationships, or + Entities.

+

You can add up to 5 different data sets. The Label field allows you to name a data set, and this label can then be shown as a legend in the widget using the Display legend button in the widget parameters (see the next section).

+

Use case 2: break down your chart

+

As mentioned in How to choose the appropriate widget visualization for your use case? section you can add data sets to decompose your graph into smaller meaningful chunks. In the below points, you can find some use cases that will help you understand how to structure your data.

+

You can break down a view either by entity or by relations, depending on what you need to count.

+

Break down by entity

+

Use case example: I need to understand what are the most targeted countries by malware, and have a breakdown for each country by malware type.

+

Process:

+
    +
  1. To achieve this use case, you first need to select the horizontal bar vizualisation.
  2. +
  3. Then you need to select the knowledge graph perspective.
  4. +
+

In the filters view:

+
    +
  1. Then input your main query Source type = Malware AND Target type = Countries AND Relation type = Targets. Add a label to your dataset.
  2. +
  3. Add an entity data set by using access button + Entities.
  4. +
  5. Add the following filters Entity type = Malware AND In regards of = targets. Add a label to your dataset.
  6. +
+

filter view

+

In the parameter view:

+
    +
  1. Attribute (of your relation) = entity (so that you display the different entities values)
  2. +
  3. Display the source toggle = off
  4. +
  5. Attribute (of your entity malware) = Malware type (since you want to break down your relations by the malware types)
  6. +
+

parameter view

+

As a result, you get a list of countries broken down by malware types.

+

final result

+

Break down by relation

+

Use case example: I need to understand what are the top targeting malware and have a breakdown of the top targets per malware

+

Process:

+
    +
  1. To achieve this use case, you first need to select the horizontal bar vizualisation.
  2. +
  3. Then you need to select the knowledge graph perspective.
  4. +
+

In the filters view:

+
    +
  1. Then input your main query Source type = Malware AND Relation type = Targets. Add a label to your dataset.
  2. +
  3. Add a relation data set by using access button + Relationships
  4. +
  5. Add the following filters Source type = Malware AND Relation type = targets. Add a label to your dataset.
  6. +
+

filter view

+

In the parameter view:

+
    +
  1. Attribute (of your relation): entity (so that you display the different entities values)
  2. +
  3. Display the source toggle = on
  4. +
  5. Attribute (of your entity malware) = Malware type (since you want to break down your relations by the malware types)
  6. +
  7. Display the source toggle = off
  8. +
+

paramter view

+

As a result, you get a list of malware with the breakdown of their top targets.

+

final view

+

More use cases

+

To see more use cases, feel free to have a look at this blog post that will provide you additional information.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/widgets/index.html b/6.2.X/usage/widgets/index.html new file mode 100755 index 00000000..3727313f --- /dev/null +++ b/6.2.X/usage/widgets/index.html @@ -0,0 +1,5231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Widget creation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Widget creation

+

Creating widgets on the dashboard involves a four-step configuration process. By navigating through these configuration steps, users can design widgets that meet their specific requirements.

+

Widget configuration

+

1. Visualization

+

Users can select from 15 diverse visualization options to highlight different aspects of their data. This includes simple views like counters and lists, as well as more intricate views like heatmaps and trees. The chosen visualization impacts the available perspectives and parameters, making it crucial to align the view with the desired data observations. Here are a few insights:

+
    +
  • Line and Area views: Ideal for visualizing activity volumes over time.
  • +
  • Horizontal bar views: Designed to identify top entities that best satisfy applied filters (e.g., top malware targeting the Finance sector).
  • +
  • Tree views: Useful for comparing activity volumes.
  • +
  • ...
  • +
+

Widget visualization

+

2. Perspective

+

A perspective is the way the platform will count the data to display in your widgets:

+
    +
  • Entities Perspective: Focuses on entities, allowing observation of simple knowledge based on defined filters and criteria. The count will be based on entities only.
  • +
  • Knowledge Graph Perspective: Concentrates on relationships, displaying intricate knowledge derived from relationships between entities and specified filters. The count will be based on relations only.
  • +
  • Activity & History Perspective: Centers on activities within the platform, not the knowledge content. This perspective is valuable for monitoring user and connector activities, evaluating data sources, and more.
  • +
+

Widget perspective

+

3. Filters

+

Generic knowledge

+

Filters vary based on the selected perspective, defining the dataset to be utilized in the widget. Filters are instrumental in narrowing down the scope of data for a more focused analysis.

+

While filters in the "Entities" and "Activity & History" perspectives align with the platform's familiar search and feed creation filters, the "Knowledge Graph" perspective introduces a more intricate filter configuration.Therefore, they need to be addressed in more detail.

+

Filter in the context of Knowledge Graph

+

Two types of filters are available in the Knowledge Graph perspective:

+
    +
  • +

    Main query filter

    +
      +
    • Classic filters (gray): Define the relationships to be retrieved, forming the basis on which the widget displays data. Remember, statistics in the Knowledge Graph perspective are based on relationships.
    • +
    +
  • +
  • +

    Pre-query filters

    +
      +
    • Pre-query filters are used to provide to your main query a specific dataset. In other words, instead of making a query on the whole data set of your platform, you can already target a subset of data that will match certain criteria. They are two types of pre-query filters:
        +
      • Dynamic filters on the source (orange): Refine data by filtering on entities positioned as the source (in the "from" position) of the relationship.
      • +
      • Dynamic filters on the target (green): Refine data by filtering on entities positioned as the target (in the "to" position) of the relationship.
      • +
      +
    • +
    +
  • +
+
+

Pre-query limitation

+

The pre-query is limited to 5000 results. If your pre-query results in having more than 5000 results, your widget will only display statistics based on these 5000 results matching your pre-query, resulting in a wrong view. To avoid this issue, be specific in your pre-query filters.

+
+

Example scenario:

+

Let's consider an example scenario: Analyzing the initial access attack patterns used by intrusion sets targeting the finance sector.

+
    +
  1. Classic filters: Define the relationships associated with the use of attack patterns by intrusion sets
  2. +
  3. Dynamic filters on the source (Orange): Narrow down the data by filtering on intrusion sets targeting the finance sector.
  4. +
  5. Dynamic filters on the target (Green): Narrow down the data by filtering on attack patterns associated with the kill chain's initial access phase.
  6. +
+

By leveraging these advanced filters, users can conduct detailed analyses within the Knowledge Graph perspective, unlocking insights that are crucial for understanding intricate relationships and statistics.

+

Widget filters

+

In certain views, you can access buttons like +, + Relationships, or + Entities. These buttons enable you to incorporate different data into the same widget for comparative analysis. For instance, in a Line view, adding a second set of filters will display two curves in the widget, each corresponding to one of the filtered data sets. Depending on the view, you can work with 1 to 5 sets of filters. The Label field allows you to name a data set, and this label can then be shown as a legend in the widget using the Display legend button in the widget parameters (see the next section).

+

Widget multiple filters

+

4. Parameters

+

Parameters depend on the chosen visualization and allow users to define widget titles, choose displayed elements from the filtered data, select data reference date, and configure various other parameters specific to each visualization.

+

For the "Knowledge Graph" perspective, a critical parameter is the Display the source toggle. This feature empowers users to choose whether the widget displays entities from the source side or the target side of the relationships.

+
    +
  • Toggle ON ("Display the source"): The widget focuses on entities positioned as the source of the relationships (in the "from" position).
  • +
  • Toggle OFF ("Display the target"): The widget shifts its focus to entities positioned as the target of the relationships (in the "to" position).
  • +
+

This level of control ensures that your dashboard aligns precisely with your analytical objectives, offering a tailored perspective based on your data and relationship.

+

Widget parameters

+

Prerequisite knowledge

+

To successfully configure widgets in OpenCTI, having a solid understanding of the platform's data modeling is essential. Knowing specific relationships, entities, and their attributes helps refine filters accurately. Let's explore two examples.

+

Scenarios 1:

+

Consider the scenario where you aim to visualize relationships between intrusion sets and attack patterns. In this case, the relevant relationship type connecting intrusion sets to attack patterns is labeled as "Uses" (as illustrated in the "Filters" section).

+

Scenarios 2:

+

Suppose your goal is to retrieve all reports associated with the finance sector. In this case, it's essential to use the correct filter for the finance sector. Instead of placing the finance sector in the "Related entity" filter, it should be placed in the "Contains" filter. Since a Report is a container object (like Cases and Groupings), it contains entities within it and is not related to entities.

+

Key data modeling aspects

+
    +
  • Entities: Recognizing container (e.g. Reports, Cases and Groupings) and understanding the difference with non-container.
  • +
  • Relationships: Identifying the relationship types connecting entities.
  • +
  • Attributes: Understanding entities and relationships attributes for effective filtering.
  • +
+

Having this prerequisite knowledge allows you to navigate the widget configuration process seamlessly, ensuring accurate and insightful visualizations based on your specific data requirements.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/workbench/index.html b/6.2.X/usage/workbench/index.html new file mode 100755 index 00000000..615d2b9f --- /dev/null +++ b/6.2.X/usage/workbench/index.html @@ -0,0 +1,5040 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Analyst workbench - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Analyst workbench

+

Workbenches serve as dedicated workspaces for manipulating data before it is officially imported into the platform.

+

Location of use

+

The workbenches are located at various places within the platform:

+

Data import and analyst workbenches window

+

This window encompasses all the necessary tools for importing a file. Files imported through this interface will subsequently be processed by the import connectors, resulting in the creation of workbenches. Additionally, analysts can manually create a workbench by clicking on the "+" icon at the bottom right of the window.

+

Data import and workbenches panel

+

Data tabs of all entities

+

Workbenches are also accessible through the "Data" tabs of entities, providing convenient access to import data associated with the entity.

+

Workbench in "Data" tab

+

Operation

+

Workbenches are automatically generated upon the import of a file through an import connector. When an import connector is initiated, it scans files for recognizable entities and subsequently creates a workbench. All identified entities are placed within this workbench for analyst reviews. +Alternatively, analysts have the option to manually create a workbench by clicking on the "+" icon at the bottom right of the "Data import and analyst workbenches" window.

+

Overview of workbench

+

The workbench being a draft space, the analysts use it to review connector proposals before finalizing them for import. Within the workbench, analysts have the flexibility to add, delete, or modify entities to meet specific requirements.

+

Workbench data manipulation

+

Once the content within the workbench is deemed acceptable, the analyst must initiate the ingestion process by clicking on Validate this workbench. This action signifies writing the data in the knowledge base.

+
+

Workbenches are drafting spaces

+

Until the workbench is validated, the contained data remains in draft form and is not recorded in the knowledge base. This ensures that only reviewed and approved data is officially integrated into the platform.

+
+

For more information on importing files, refer to the Import from files documentation page.

+

Confidence level of created knowledge through workbench

+

The confidence level of knowledge created through workbench is affected by the confidence level of the user. Please navigate to this page to understand in more details.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.2.X/usage/workflows/index.html b/6.2.X/usage/workflows/index.html new file mode 100755 index 00000000..21d6a767 --- /dev/null +++ b/6.2.X/usage/workflows/index.html @@ -0,0 +1,4966 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Workflows and assignation - OpenCTI Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

Workflows and assignation

+

Efficiently manage and organize your work within the OpenCTI platform by leveraging workflows and assignment. These capabilities provide a structured approach to tracking the status of objects and assigning responsibilities to users.

+

Workflows

+

Workflows are designed to trace the status of objects in the system. They are represented by the "Processing status" field embedded in each object. By default, this field is disabled for most objects but can be activated through the platform settings. For details on activating and configuring workflows, refer to the dedicated documentation page.

+

Enabling workflows enhances visibility into the progress and status of different objects, providing a comprehensive view for effective management.

+

Processing status

+

Assignment attributes

+

Certain objects, including Reports, Cases, and Tasks, come equipped with "Assignees" and "Participants" attributes. These attributes serve the purpose of designating individuals responsible for the object and those who actively participate in it.

+

Attributes can be set as mandatory or with default values, streamlining the assignment process. Users can also be assigned or designated as participants manually, contributing to a collaborative and organized workflow. For details on configuring attributes, refer to the dedicated documentation page.

+

Assignment attributes

+

Users can stay informed about assignments through notification triggers. By setting up notification triggers, users receive alerts when an object is assigned to them. This ensures timely communication and proactive engagement with assigned tasks or responsibilities.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/latest b/latest index de3ddd9c..5ad0bc81 120000 --- a/latest +++ b/latest @@ -1 +1 @@ -6.1.X \ No newline at end of file +6.2.X \ No newline at end of file diff --git a/versions.json b/versions.json index e694b21a..38ef65da 100644 --- a/versions.json +++ b/versions.json @@ -1,11 +1,16 @@ [ { - "version": "6.1.X", - "title": "6.1.X", + "version": "6.2.X", + "title": "6.2.X", "aliases": [ "latest" ] }, + { + "version": "6.1.X", + "title": "6.1.X", + "aliases": [] + }, { "version": "6.0.X", "title": "6.0.X",