77use Symfony \Component \HttpFoundation \JsonResponse ;
88use Symfony \Component \Routing \Annotation \Route ;
99use Symfony \Component \Serializer \SerializerInterface ;
10+ use Symfony \Component \Yaml \Yaml ;
11+ use Symfony \Component \Yaml \Exception \ParseException ;
1012use Doctrine \ORM \EntityManagerInterface ;
1113use App \Entity \SigmaRule ;
14+ use App \Entity \SigmaRuleVersion ;
15+ use App \Service \SigmaRuleValidator ;
1216
1317class SigmaController extends AbstractController{
1418
1519 private $ entityManger ;
1620 private $ serializer ;
21+ private $ validator ;
1722
18- public function __construct (EntityManagerInterface $ em , SerializerInterface $ serializer ){
23+ public function __construct (EntityManagerInterface $ em , SerializerInterface $ serializer, SigmaRuleValidator $ validator ){
1924 $ this ->entityManger = $ em ;
2025 $ this ->serializer = $ serializer ;
26+ $ this ->validator = $ validator ;
27+ }
28+
29+
30+
31+ #[Route('/api/rules/sigma/add_rule ' , name: 'app_sigma_add_rule ' , methods: ['POST ' ])]
32+ public function SaveNewSigmaRule (Request $ request ): JsonResponse {
33+ $ data = json_decode ($ request ->getContent (), true );
34+ if (!isset ($ data ['rule_content ' ])) {
35+ return new JsonResponse (['error ' => 'Missing rule content ' ], Response::HTTP_BAD_REQUEST );
36+ }
37+
38+ $ ruleContent = $ data ['rule_content ' ];
39+ $ validationResult = $ this ->validator ->validateSigmaRuleContent ($ ruleContent );
40+
41+ if (isset ($ validationResult ['error ' ])) {
42+ return new JsonResponse (['error ' => $ validationResult ['error ' ]], Response::HTTP_BAD_REQUEST );
43+ }
44+
45+ if (!empty ($ validationResult ['missingFields ' ])) {
46+ $ missingFieldsString = implode (", " , $ validationResult ['missingFields ' ]);
47+ return new JsonResponse (['error ' => "Missing required fields: " . $ missingFieldsString ], Response::HTTP_BAD_REQUEST );
48+ }
49+
50+ $ yamlData = $ validationResult ['yamlData ' ];
51+
52+ $ existingRule = $ this ->entityManger ->getRepository (SigmaRule::class)->findOneBy (['title ' => $ yamlData ['title ' ]]);
53+ if ($ existingRule ) {
54+ return new JsonResponse (['error ' => 'A rule with this title already exists ' ], Response::HTTP_BAD_REQUEST );
55+ }
56+
57+ $ newRule = new SigmaRule ();
58+ $ newRuleVersion = new SigmaRuleVersion ();
59+ $ newRule ->setTitle ($ yamlData ['title ' ]);
60+ $ newRule ->setDescription ($ yamlData ['description ' ]);
61+ $ newRule ->setActive (false );
62+ $ newRuleVersion ->setContent (Yaml::dump ($ yamlData , 4 ));
63+ $ newRuleVersion ->setLevel ($ yamlData ['level ' ]);
64+ $ newRule ->addVersion ($ newRuleVersion );
65+
66+ $ existingVersion = $ this ->entityManger ->getRepository (SigmaRuleVersion::class)->findOneBy (['hash ' => $ newRuleVersion ->getHash ()]);
67+ if ($ existingVersion ) {
68+ return new JsonResponse (['error ' => 'A rule with the exact same content already exists ' ], Response::HTTP_BAD_REQUEST );
69+ }
70+
71+ try {
72+ $ this ->entityManger ->persist ($ newRule );
73+ $ this ->entityManger ->flush ();
74+ } catch (\Exception $ e ) {
75+ return new JsonResponse (['error ' => 'Failed to save the rule: ' . $ e ->getMessage ()], Response::HTTP_INTERNAL_SERVER_ERROR );
76+ }
77+
78+ return new JsonResponse (['error ' => '' , 'rule_id ' => $ newRule ->getId ()], Response::HTTP_OK );
2179 }
2280
2381
@@ -29,7 +87,7 @@ public function listRulesSummary(Request $request): Response
2987 }
3088
3189 #[Route('/api/rules/sigma/{ruleId}/status ' , name:'app_sigma_change_rule_status ' , methods: ['PUT ' ])]
32- public function editRuleStatus (Request $ request , int $ ruleId ): Response {
90+ public function editRuleStatus (Request $ request , int $ ruleId ): JsonResponse {
3391 $ rule = $ this ->entityManger ->getRepository (SigmaRule::class)->find ($ ruleId );
3492 if (!$ rule ){
3593 return new JsonResponse (['error ' => 'Rule not found ' ], Response::HTTP_NOT_FOUND );
@@ -44,7 +102,7 @@ public function editRuleStatus(Request $request, int $ruleId): Response{
44102 }
45103
46104 #[Route('api/rules/sigma/{ruleId}/details ' , name:'app_sigma_get_rule ' , methods: ['GET ' ])]
47- public function getRule (Request $ request , int $ ruleId ): Response {
105+ public function getRule (Request $ request , int $ ruleId ): JsonResponse {
48106 $ rule = $ this ->entityManger ->getRepository (SigmaRule::class)->find ($ ruleId );
49107 if (!$ rule ) {
50108 return new JsonResponse (['error ' => 'Rule not found ' ], Response::HTTP_NOT_FOUND );
@@ -54,8 +112,77 @@ public function getRule(Request $request, int $ruleId): Response {
54112 return new JsonResponse ($ serializedRule , Response::HTTP_OK , [], true );
55113 }
56114
115+ #[Route('/api/rules/sigma/{ruleId}/add_version ' , name: 'app_sigma_add_version ' , methods: ['POST ' ])]
116+ public function addRuleVersion (Request $ request , int $ ruleId ): JsonResponse {
117+ $ rule = $ this ->entityManger ->getRepository (SigmaRule::class)->find ($ ruleId );
118+ if (!$ rule ) {
119+ return new JsonResponse (['error ' => 'Rule not found ' ], Response::HTTP_NOT_FOUND );
120+ }
121+
122+ $ data = json_decode ($ request ->getContent (), true );
123+ if (!isset ($ data ['rule_content ' ])) {
124+ return new JsonResponse (['error ' => 'Missing rule content ' ], Response::HTTP_BAD_REQUEST );
125+ }
126+
127+ $ ruleContent = $ data ['rule_content ' ];
128+ $ validationResult = $ this ->validator ->validateSigmaRuleContent ($ ruleContent );
129+
130+ if (isset ($ validationResult ['error ' ])) {
131+ return new JsonResponse (['error ' => $ validationResult ['error ' ]], Response::HTTP_BAD_REQUEST );
132+ }
133+
134+ if (!empty ($ validationResult ['missingFields ' ])) {
135+ $ missingFieldsString = implode (", " , $ validationResult ['missingFields ' ]);
136+ return new JsonResponse (['error ' => "Missing required fields: " . $ missingFieldsString ], Response::HTTP_BAD_REQUEST );
137+ }
138+
139+ $ yamlData = $ validationResult ['yamlData ' ];
140+
141+ $ newRuleVersion = new SigmaRuleVersion ();
142+ $ newRuleVersion ->setContent ($ ruleContent );
143+ $ newRuleVersion ->setLevel ($ yamlData ['level ' ]);
144+ $ newRuleVersion ->setRule ($ rule );
145+
146+ $ existingVersion = $ this ->entityManger ->getRepository (SigmaRuleVersion::class)->findOneBy (['hash ' => $ newRuleVersion ->getHash ()]);
147+ if ($ existingVersion ) {
148+ return new JsonResponse (['error ' => 'A version with the exact same content already exists ' ], Response::HTTP_BAD_REQUEST );
149+ }
150+
151+ if ($ rule ->getTitle () !== $ yamlData ['title ' ]) {
152+ $ rule ->setTitle ($ yamlData ['title ' ]);
153+ }
154+ if ($ rule ->getDescription () !== $ yamlData ['description ' ]) {
155+ $ rule ->setDescription ($ yamlData ['description ' ]);
156+ }
157+
158+ $ existingRulesWithTitle = $ this ->entityManger ->getRepository (SigmaRule::class)->findBy (['title ' => $ yamlData ['title ' ]]);
159+ foreach ($ existingRulesWithTitle as $ existingRule ) {
160+ if ($ existingRule ->getId () !== $ rule ->getId ()) {
161+ return new JsonResponse (['error ' => 'Another rule with this title already exists ' ], Response::HTTP_BAD_REQUEST );
162+ }
163+ }
164+
165+ $ latestVersion = $ this ->entityManger ->getRepository (SigmaRuleVersion::class)->findOneBy (
166+ ['rule ' => $ rule ],
167+ ['id ' => 'DESC ' ]
168+ );
169+ if ($ latestVersion && $ latestVersion ->getHash () === $ newRuleVersion ->getHash ()) {
170+ return new JsonResponse (['error ' => 'The new version content is identical to the latest version ' ], Response::HTTP_BAD_REQUEST );
171+ }
172+
173+
174+ try {
175+ $ this ->entityManger ->persist ($ newRuleVersion );
176+ $ this ->entityManger ->flush ();
177+ } catch (\Exception $ e ) {
178+ return new JsonResponse (['error ' => 'Failed to save the new version: ' . $ e ->getMessage ()], Response::HTTP_INTERNAL_SERVER_ERROR );
179+ }
180+
181+ return new JsonResponse (['error ' => '' , 'version_id ' => $ newRuleVersion ->getId ()], Response::HTTP_OK );
182+ }
183+
57184 #[Route('api/rules/sigma/{ruleId} ' , name:'app_sigma_delete_rule ' , methods: ['DELETE ' ])]
58- public function deleteRule (Request $ request , int $ ruleId ): Response {
185+ public function deleteRule (Request $ request , int $ ruleId ): JsonResponse {
59186 $ rule = $ this ->entityManger ->getRepository (SigmaRule::class)->find ($ ruleId );
60187 if (!$ rule ) {
61188 return new JsonResponse (['error ' => 'Rule not found ' ], Response::HTTP_NOT_FOUND );
0 commit comments