import { Transform } from 'stream' ; import * as t from 'io-ts' ; import { has } from 'lodash/fp' ; import { pipe } from 'fp-ts/lib/pipeable' ; import { fold } from 'fp-ts/lib/Either' ; import { ExportExceptionDetails , ImportExceptionListItemSchema , ImportExceptionListItemSchemaDecoded , ImportExceptionListSchemaDecoded , ImportExceptionsListSchema , importExceptionListItemSchema , importExceptionsListSchema , } from '@kbn/securitysolution-io-ts-list-types' ; import { createConcatStream , createFilterStream , createMapStream , createReduceStream , createSplitStream , } from '@kbn/utils' ; import { BadRequestError } from '@kbn/securitysolution-es-utils' ; import { exactCheck , formatErrors } from '@kbn/securitysolution-io-ts-utils' ; import { ExceptionsImport } from '../../import_exception_list_and_items' ; /**
 * Parses strings from ndjson stream
 */ export const parseNdjsonStrings = ( ) : Transform => { return createMapStream ( ( ndJsonStr ) : Transform => { try { return JSON . parse ( ndJsonStr ) ; } catch ( err ) { return err ; } } ) ; } ; /**
 *
 * Sorting of exceptions logic
 *
 */ param exception returns { boolean } export const isImportExceptionListItemSchema = ( exception ) : exception is ImportExceptionListItemSchema => { return has ( 'entries' , exception ) || has ( 'item_id' , exception ) ; } ; param exceptions { array } returns { stream } export const sortExceptions = ( exceptions ) : { items : ImportExceptionListItemSchema [ ] ; lists : ImportExceptionsListSchema [ ] ; } => { return exceptions . reduce < { items : ImportExceptionListItemSchema [ ] ; lists : ImportExceptionsListSchema [ ] ; } > ( ( acc , exception ) => { if ( isImportExceptionListItemSchema ( exception ) ) { return { ... acc , items : [ ... acc . items , exception ] } ; } else { return { ... acc , lists : [ ... acc . lists , exception ] } ; } } , { items : [ ] , lists : [ ] , } ) ; } ; returns { stream } export const sortExceptionsStream = ( ) : Transform => { return createReduceStream < { items : Array < ImportExceptionListItemSchema | Error > ; lists : Array < ImportExceptionsListSchema | Error > ; } > ( ( acc , exception ) => { if ( has ( 'entries' , exception ) || has ( 'item_id' , exception ) ) { return { ... acc , items : [ ... acc . items , exception ] } ; } else { return { ... acc , lists : [ ... acc . lists , exception ] } ; } } , { items : [ ] , lists : [ ] , } ) ; } ; /**
 *
 * Validating exceptions logic
 *
 */ returns { stream } export const validateExceptionsStream = ( ) : Transform => { return createMapStream < { items : Array < ImportExceptionListItemSchema | Error > ; lists : Array < ImportExceptionsListSchema | Error > ; } > ( ( exceptions ) => ( { items : validateExceptionsItems ( exceptions . items ) , lists : validateExceptionsLists ( exceptions . lists ) , } ) ) ; } ; param exceptions { array } returns { object } export const validateExceptions = ( exceptions ) : { items : Array < ImportExceptionListItemSchemaDecoded | Error > ; lists : Array < ImportExceptionListSchemaDecoded | Error > ; } => { return { items : validateExceptionsItems ( exceptions . items ) , lists : validateExceptionsLists ( exceptions . lists ) , } ; } ; param lists { array } returns { array } export const validateExceptionsLists = ( lists ) : Array < ImportExceptionListSchemaDecoded | Error > => { const onLeft = ( errors ) : BadRequestError | ImportExceptionListSchemaDecoded => { return new BadRequestError ( formatErrors ( errors ) . join ( ) ) ; } ; const onRight = ( schemaList ) : BadRequestError | ImportExceptionListSchemaDecoded => { return schemaList as ImportExceptionListSchemaDecoded ; } ; return lists . map ( ( obj ) => { if ( ! ( obj instanceof Error ) ) { const decodedList = importExceptionsListSchema . decode ( obj ) ; const checkedList = exactCheck ( obj , decodedList ) ; return pipe ( checkedList , fold ( onLeft , onRight ) ) ; } else { return obj ; } } ) ; } ; param items { array } returns { array } export const validateExceptionsItems = ( items ) : Array < ImportExceptionListItemSchemaDecoded | Error > => { const onLeft = ( errors ) : BadRequestError | ImportExceptionListItemSchemaDecoded => { return new BadRequestError ( formatErrors ( errors ) . join ( ) ) ; } ; const onRight = ( itemSchema ) : BadRequestError | ImportExceptionListItemSchemaDecoded => { return itemSchema as ImportExceptionListItemSchemaDecoded ; } ; return items . map ( ( item ) => { if ( ! ( item instanceof Error ) ) { const decodedItem = importExceptionListItemSchema . decode ( item ) ; const checkedItem = exactCheck ( item , decodedItem ) ; return pipe ( checkedItem , fold ( onLeft , onRight ) ) ; } else { return item ; } } ) ; } ; /**
 *
 * Validating import limits logic
 *
 */ param limit { number } returns { array } export const checkLimits = ( limit ) : ( ( arg : ExceptionsImport ) => ExceptionsImport ) => { return ( exceptions ) : ExceptionsImport => { if ( exceptions . length >= limit ) { throw new Error ( `Can't import more than ${ limit } exceptions` ) ; } return exceptions ; } ; } ; param limit { number } returns { stream } export const createLimitStream = ( limit ) : Transform => { return new Transform ( { objectMode : true , async transform ( obj , _ , done ) { if ( obj . lists . length + obj . items . length >= limit ) { done ( new Error ( `Can't import more than ${ limit } exceptions` ) ) ; } else { done ( undefined , obj ) ; } } , } ) ; } ; /**
 *
 * Filters
 *
 */ /**
 * Filters out the counts metadata added on export
 */ export const filterExportedCounts = ( ) : Transform => { return createFilterStream < ImportExceptionListSchemaDecoded | ImportExceptionListItemSchemaDecoded | ExportExceptionDetails > ( ( obj ) => obj != null && ! has ( 'exported_exception_list_count' , obj ) ) ; } ; /**
 * Filters out empty strings from ndjson stream
 */ export const filterEmptyStrings = ( ) : Transform => { return createFilterStream < string > ( ( ndJsonStr ) => ndJsonStr . trim ( ) !== '' ) ; } ; /**
 *
 * Set of helpers to run exceptions through on import
 *
 */ param exceptionsToImport { array } param exceptionsLimit { number } returns { object } export const exceptionsChecksFromArray = ( exceptionsToImport , exceptionsLimit ) : { items : Array < ImportExceptionListItemSchemaDecoded | Error > ; lists : Array < ImportExceptionListSchemaDecoded | Error > ; } => { return pipe ( exceptionsToImport , checkLimits ( exceptionsLimit ) , sortExceptions , validateExceptions ) ; } ; param exceptionsToImport { array } param exceptionsLimit { number } returns { object } export const createExceptionsStreamFromNdjson = ( exceptionsLimit ) : Transform [ ] => { return [ createSplitStream ( '\n' ) , filterEmptyStrings ( ) , parseNdjsonStrings ( ) , filterExportedCounts ( ) , sortExceptionsStream ( ) , validateExceptionsStream ( ) , createLimitStream ( exceptionsLimit ) , createConcatStream ( [ ] ) , ] ; } ;