1111import com .park .utmstack .service .dto .correlation .AdversaryType ;
1212import com .park .utmstack .service .dto .correlation .UtmCorrelationRulesDTO ;
1313import com .park .utmstack .service .dto .correlation .UtmCorrelationRulesMapper ;
14+ import com .park .utmstack .service .dto .correlation .RuleYaml ;
1415import com .park .utmstack .service .logstash_filter .UtmLogstashFilterService ;
15- import lombok .Data ;
1616import lombok .RequiredArgsConstructor ;
1717import org .slf4j .Logger ;
1818import org .slf4j .LoggerFactory ;
2121import org .springframework .transaction .annotation .Transactional ;
2222import org .yaml .snakeyaml .Yaml ;
2323
24- import javax .annotation .PostConstruct ;
2524import java .io .IOException ;
26- import java .lang .Exception ;
2725import java .nio .file .Files ;
2826import java .nio .file .Path ;
2927import java .nio .file .Paths ;
@@ -172,57 +170,69 @@ private Set<String> syncRules() {
172170 paths .filter (path -> Files .isRegularFile (path ) && isYamlFile (path )).forEach (path -> {
173171 try {
174172 String content = Files .readString (path );
175- RuleYaml ruleYaml = yaml .loadAs (content , RuleYaml .class );
176- if (ruleYaml == null || ruleYaml .getName () == null ) {
177- log .warn ("Skipping invalid rule file: {}" , path );
178- return ;
173+ Object yamlObj = yaml .load (content );
174+ List <Map <String , Object >> rawRules = new ArrayList <>();
175+
176+ if (yamlObj instanceof List ) {
177+ rawRules .addAll ((List <Map <String , Object >>) yamlObj );
178+ } else if (yamlObj instanceof Map ) {
179+ rawRules .add ((Map <String , Object >) yamlObj );
179180 }
180181
181- foundRules .add (ruleYaml .getName ());
182- Optional <UtmCorrelationRules > ruleOpt = rulesRepository .findOneByRuleName (ruleYaml .getName ());
183- UtmCorrelationRulesDTO ruleDto = new UtmCorrelationRulesDTO ();
182+ for (Map <String , Object > rawRule : rawRules ) {
183+ RuleYaml ruleYaml = mapToRuleYaml (rawRule );
184+ if (ruleYaml == null || ruleYaml .getName () == null ) {
185+ log .warn ("Skipping invalid rule in file: {}" , path );
186+ continue ;
187+ }
184188
185- if (ruleOpt .isPresent ()) {
186- ruleDto .setId (ruleOpt .get ().getId ());
187- } else {
188- ruleDto .setId (rulesService .getSystemSequenceNextValue ());
189- }
189+ foundRules .add (ruleYaml .getName ());
190+ Optional <UtmCorrelationRules > ruleOpt = rulesRepository .findOneByRuleName (ruleYaml .getName ());
191+ UtmCorrelationRulesDTO ruleDto = new UtmCorrelationRulesDTO ();
190192
191- ruleDto .setName (ruleYaml .getName ());
192- ruleDto .setCategory (ruleYaml .getCategory ());
193- ruleDto .setTechnique (ruleYaml .getTechnique ());
194- ruleDto .setAdversary (ruleYaml .getAdversary () != null ? ruleYaml .getAdversary () : AdversaryType .origin );
195- ruleDto .setDescription (ruleYaml .getDescription ());
196- ruleDto .setReferences (ruleYaml .getReferences ());
197- ruleDto .setDefinition (ruleYaml .getWhere ());
198- ruleDto .setGroupBy (ruleYaml .getGroupBy ());
199- ruleDto .setDeduplicateBy (ruleYaml .getDeduplicateBy ());
200- ruleDto .setAfterEvents (ruleYaml .getAfterEvents ());
201- ruleDto .setSystemOwner (true );
202- ruleDto .setRuleActive (true );
203-
204- if (ruleYaml .getImpact () != null ) {
205- ruleDto .setConfidentiality (ruleYaml .getImpact ().getConfidentiality ());
206- ruleDto .setIntegrity (ruleYaml .getImpact ().getIntegrity ());
207- ruleDto .setAvailability (ruleYaml .getImpact ().getAvailability ());
208- }
193+ if (ruleOpt .isPresent ()) {
194+ ruleDto .setId (ruleOpt .get ().getId ());
195+ } else if (ruleYaml .getId () != null ) {
196+ ruleDto .setId (ruleYaml .getId ());
197+ } else {
198+ ruleDto .setId (rulesService .getSystemSequenceNextValue ());
199+ }
209200
210- // Map dataTypes strings to UtmDataTypes entities
211- if (ruleYaml .getDataTypes () != null ) {
212- Set <UtmDataTypes > dataTypes = ruleYaml .getDataTypes ().stream ()
213- .map (dtName -> dataTypesRepository .findOneByDataType (dtName .toLowerCase ()))
214- .filter (Optional ::isPresent )
215- .map (Optional ::get )
216- .collect (Collectors .toSet ());
217- ruleDto .setDataTypes (dataTypes );
218- }
201+ ruleDto .setName (ruleYaml .getName ());
202+ ruleDto .setCategory (ruleYaml .getCategory ());
203+ ruleDto .setTechnique (ruleYaml .getTechnique ());
204+ ruleDto .setAdversary (ruleYaml .getAdversary () != null ? ruleYaml .getAdversary () : AdversaryType .origin );
205+ ruleDto .setDescription (ruleYaml .getDescription ());
206+ ruleDto .setReferences (ruleYaml .getReferences ());
207+ ruleDto .setDefinition (ruleYaml .getWhere ());
208+ ruleDto .setGroupBy (ruleYaml .getGroupBy ());
209+ ruleDto .setDeduplicateBy (ruleYaml .getDeduplicateBy ());
210+ ruleDto .setAfterEvents (ruleYaml .getAfterEvents ());
211+ ruleDto .setSystemOwner (true );
212+ ruleDto .setRuleActive (true );
213+
214+ if (ruleYaml .getImpact () != null ) {
215+ ruleDto .setConfidentiality (ruleYaml .getImpact ().getConfidentiality ());
216+ ruleDto .setIntegrity (ruleYaml .getImpact ().getIntegrity ());
217+ ruleDto .setAvailability (ruleYaml .getImpact ().getAvailability ());
218+ }
219219
220- UtmCorrelationRules entity = rulesMapper .toEntity (ruleDto );
221- if (ruleOpt .isPresent ()) {
222- rulesService .updateRule (entity , true );
220+ // Map dataTypes strings to UtmDataTypes entities
221+ if (ruleYaml .getDataTypes () != null ) {
222+ Set <UtmDataTypes > dataTypes = ruleYaml .getDataTypes ().stream ()
223+ .map (dtName -> dataTypesRepository .findOneByDataType (dtName .toLowerCase ()))
224+ .filter (Optional ::isPresent )
225+ .map (Optional ::get )
226+ .collect (Collectors .toSet ());
227+ ruleDto .setDataTypes (dataTypes );
228+ }
223229
224- } else {
225- rulesService .save (entity , true );
230+ UtmCorrelationRules entity = rulesMapper .toEntity (ruleDto );
231+ if (ruleOpt .isPresent ()) {
232+ rulesService .updateRule (entity , true );
233+ } else {
234+ rulesService .save (entity , true );
235+ }
226236 }
227237
228238 } catch (Exception e ) {
@@ -235,6 +245,12 @@ private Set<String> syncRules() {
235245 return foundRules ;
236246 }
237247
248+ private RuleYaml mapToRuleYaml (Map <String , Object > map ) {
249+ Yaml yaml = new Yaml ();
250+ String dump = yaml .dump (map );
251+ return yaml .loadAs (dump , RuleYaml .class );
252+ }
253+
238254 private void cleanupOrphanedFilters (Set <Long > currentFilterIds ) {
239255 if (currentFilterIds .isEmpty ()) return ;
240256 List <UtmLogstashFilter > systemFilters = filterRepository .findAllBySystemOwnerIsTrue ();
@@ -272,27 +288,4 @@ private String getFileNameWithoutExtension(Path path) {
272288 int lastDotIndex = fileName .lastIndexOf ('.' );
273289 return (lastDotIndex == -1 ) ? fileName : fileName .substring (0 , lastDotIndex );
274290 }
275-
276- @ Data
277- public static class RuleYaml {
278- private List <String > dataTypes ;
279- private String name ;
280- private ImpactYaml impact ;
281- private String category ;
282- private String technique ;
283- private AdversaryType adversary ;
284- private String description ;
285- private List <String > references ;
286- private String where ;
287- private List <com .park .utmstack .domain .correlation .rules .SearchRequest > afterEvents ;
288- private List <String > groupBy ;
289- private List <String > deduplicateBy ;
290- }
291-
292- @ Data
293- public static class ImpactYaml {
294- private Integer confidentiality ;
295- private Integer integrity ;
296- private Integer availability ;
297- }
298291}
0 commit comments