9696
9797#define JSON_STREAM_BUFFER_SIZE 4096
9898
99+ typedef struct ClasstypeFilter_ {
100+ char * * classtype_names ;
101+ uint32_t count ;
102+ } ClasstypeFilter ;
103+
99104typedef struct AlertJsonOutputCtx_ {
100105 LogFileCtx * file_ctx ;
101106 uint16_t flags ;
102107 uint32_t payload_buffer_size ;
103108 HttpXFFCfg * xff_cfg ;
104109 HttpXFFCfg * parent_xff_cfg ;
105110 OutputJsonCtx * eve_ctx ;
111+ ClasstypeFilter * payload_classtype_filter ;
106112} AlertJsonOutputCtx ;
107113
108114typedef struct JsonAlertLogThread_ {
@@ -640,6 +646,36 @@ static bool AlertJsonStreamData(const AlertJsonOutputCtx *json_output_ctx, JsonA
640646 return false;
641647}
642648
649+ /**
650+ * \brief Check if alert's classtype matches the payload extraction filter if filtering is configured
651+ * \param filter ClasstypeFilter to check (can be NULL)
652+ * \param pa PacketAlert containing the signature
653+ * \return true if payload should be extracted, false otherwise
654+ */
655+ static bool ShouldDumpPayloadInAlert (const ClasstypeFilter * filter , const PacketAlert * pa )
656+ {
657+ // No filter configured = always extract based on payload flags (default behavior)
658+ if (filter == NULL ) {
659+ return true;
660+ }
661+
662+ // No classtype in alert, yet filtering is configured -> no extraction
663+ if (pa -> s == NULL || pa -> s -> classtype == NULL ) {
664+ return false;
665+ }
666+
667+ // Check if classtype in alert matches any in the filter list
668+ const char * alert_classtype = pa -> s -> classtype ;
669+ for (uint32_t i = 0 ; i < filter -> count ; i ++ ) {
670+ if (strcasecmp (filter -> classtype_names [i ], alert_classtype ) == 0 ) {
671+ return true;
672+ }
673+ }
674+
675+ // No match on specified alert classtype
676+ return false;
677+ }
678+
643679static int AlertJson (ThreadVars * tv , JsonAlertLogThread * aft , const Packet * p )
644680{
645681 AlertJsonOutputCtx * json_output_ctx = aft -> json_output_ctx ;
@@ -744,8 +780,11 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
744780 }
745781 }
746782
783+ bool should_dump_payload = ShouldDumpPayloadInAlert (
784+ json_output_ctx -> payload_classtype_filter , pa );
785+
747786 /* payload */
748- if (json_output_ctx -> flags &
787+ if (should_dump_payload && json_output_ctx -> flags &
749788 (LOG_JSON_PAYLOAD | LOG_JSON_PAYLOAD_BASE64 | LOG_JSON_PAYLOAD_LENGTH )) {
750789 int stream = (p -> proto == IPPROTO_TCP ) ?
751790 (pa -> flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH ) ?
@@ -929,6 +968,27 @@ static TmEcode JsonAlertLogThreadDeinit(ThreadVars *t, void *data)
929968 return TM_ECODE_OK ;
930969}
931970
971+ /**
972+ * \brief Free classtype filter structure
973+ */
974+ static void ClasstypeFilterFree (ClasstypeFilter * filter )
975+ {
976+ if (filter == NULL ) {
977+ return ;
978+ }
979+
980+ if (filter -> classtype_names != NULL ) {
981+ for (uint32_t i = 0 ; i < filter -> count ; i ++ ) {
982+ if (filter -> classtype_names [i ] != NULL ) {
983+ SCFree (filter -> classtype_names [i ]);
984+ }
985+ }
986+ SCFree (filter -> classtype_names );
987+ }
988+
989+ SCFree (filter );
990+ }
991+
932992static void JsonAlertLogDeInitCtxSub (OutputCtx * output_ctx )
933993{
934994 SCLogDebug ("cleaning up sub output_ctx %p" , output_ctx );
@@ -940,11 +1000,85 @@ static void JsonAlertLogDeInitCtxSub(OutputCtx *output_ctx)
9401000 if (xff_cfg != NULL ) {
9411001 SCFree (xff_cfg );
9421002 }
1003+ ClasstypeFilterFree (json_output_ctx -> payload_classtype_filter );
9431004 SCFree (json_output_ctx );
9441005 }
9451006 SCFree (output_ctx );
9461007}
9471008
1009+ /**
1010+ * \brief Parse payload-only-classtypes filter from YAML
1011+ * \param conf Configuration node
1012+ * \return ClasstypeFilter* on success, NULL if not configured or empty
1013+ */
1014+ static ClasstypeFilter * JsonAlertParsePayloadClasstypeFilter (SCConfNode * conf )
1015+ {
1016+ if (conf == NULL ) {
1017+ return NULL ;
1018+ }
1019+
1020+ SCConfNode * payload_extraction_node = SCConfNodeLookupChild (conf , "payload-only-classtypes" );
1021+ if (payload_extraction_node == NULL ) {
1022+ return NULL ;
1023+ }
1024+
1025+ if (!SCConfNodeIsSequence (payload_extraction_node )) {
1026+ SCLogError ("payload-only-classtypes must be a list of classtype names" );
1027+ return NULL ;
1028+ }
1029+
1030+ SCConfNode * item ;
1031+ uint32_t count = 0 ;
1032+ TAILQ_FOREACH (item , & payload_extraction_node -> head , next ) {
1033+ if (item -> val != NULL && strlen (item -> val ) > 0 ) {
1034+ count ++ ;
1035+ }
1036+ }
1037+
1038+ if (count == 0 ) {
1039+ SCLogDebug ("payload_extraction is empty - treating as not configured" );
1040+ return NULL ;
1041+ }
1042+
1043+ ClasstypeFilter * filter = SCCalloc (1 , sizeof (ClasstypeFilter ));
1044+ if (filter == NULL ) {
1045+ return NULL ;
1046+ }
1047+
1048+ filter -> classtype_names = SCCalloc (count , sizeof (char * ));
1049+ if (filter -> classtype_names == NULL ) {
1050+ SCFree (filter );
1051+ return NULL ;
1052+ }
1053+
1054+ uint32_t idx = 0 ;
1055+ TAILQ_FOREACH (item , & payload_extraction_node -> head , next ) {
1056+ if (item -> val == NULL || strlen (item -> val ) == 0 ) {
1057+ continue ;
1058+ }
1059+
1060+ filter -> classtype_names [idx ] = SCStrdup (item -> val );
1061+ if (filter -> classtype_names [idx ] == NULL ) {
1062+ for (uint32_t i = 0 ; i < idx ; i ++ ) {
1063+ SCFree (filter -> classtype_names [i ]);
1064+ }
1065+ SCFree (filter -> classtype_names );
1066+ SCFree (filter );
1067+ return NULL ;
1068+ }
1069+ idx ++ ;
1070+ }
1071+
1072+ filter -> count = idx ;
1073+
1074+ SCLogInfo ("payload_extraction filter enabled for %u classtype(s)" , filter -> count );
1075+ for (uint32_t i = 0 ; i < filter -> count ; i ++ ) {
1076+ SCLogDebug (" - %s" , filter -> classtype_names [i ]);
1077+ }
1078+
1079+ return filter ;
1080+ }
1081+
9481082static void SetFlag (const SCConfNode * conf , const char * name , uint16_t flag , uint16_t * out_flags )
9491083{
9501084 DEBUG_VALIDATE_BUG_ON (conf == NULL );
@@ -995,6 +1129,8 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx, SCCon
9951129 SetFlag (conf , "verdict" , LOG_JSON_VERDICT , & flags );
9961130 SetFlag (conf , "payload-length" , LOG_JSON_PAYLOAD_LENGTH , & flags );
9971131
1132+ json_output_ctx -> payload_classtype_filter = JsonAlertParsePayloadClasstypeFilter (conf );
1133+
9981134 /* Check for obsolete flags and warn that they have no effect. */
9991135 static const char * deprecated_flags [] = { "http" , "tls" , "ssh" , "smtp" , "dnp3" , "app-layer" ,
10001136 "flow" , NULL };
0 commit comments