2828import org .geotools .api .filter .sort .SortOrder ;
2929import org .geotools .filter .text .cql2 .CQLException ;
3030import org .geotools .filter .text .ecql .ECQL ;
31+ import org .jspecify .annotations .Nullable ;
3132import org .slf4j .Logger ;
3233import org .slf4j .LoggerFactory ;
3334import org .springframework .beans .factory .annotation .Value ;
@@ -198,8 +199,18 @@ public ResponseEntity<?> extract(
198199 attributes .add (sourceFT .getDefaultGeometryAttribute ());
199200 }
200201
201- if (outputFormat == ExtractOutputFormat .XLSX ) {
202- validateExcelLimits (sourceFT , attributes , filter );
202+ // check if filter has valid syntax (it could still be invalid wrt feature type)
203+ Filter parsedCQL = null ;
204+ try {
205+ if (!StringUtils .isBlank (filter )) {
206+ parsedCQL = ECQL .toFilter (filter );
207+ }
208+ } catch (CQLException e ) {
209+ throw new ResponseStatusException (HttpStatus .BAD_REQUEST , "Invalid filter" );
210+ }
211+
212+ if (ExtractOutputFormat .XLSX .equals (outputFormat )) {
213+ validateExcelLimits (sourceFT , attributes , parsedCQL );
203214 }
204215
205216 SortOrder sortingOrder = SortOrder .ASCENDING ;
@@ -213,7 +224,7 @@ public ResponseEntity<?> extract(
213224
214225 //noinspection JvmTaintAnalysis Not a Path Traversal Sink because the clientId is validated
215226 this .createLayerExtractService .createLayerExtract (
216- clientId , sourceFT , attributes , filter , sortBy , sortingOrder , outputFormat , outputFileName );
227+ clientId , sourceFT , attributes , parsedCQL , sortBy , sortingOrder , outputFormat , outputFileName );
217228
218229 //noinspection JvmTaintAnalysis Not an XSS sink because the response is a json message
219230 return ResponseEntity .accepted ()
@@ -227,9 +238,9 @@ public ResponseEntity<?> extract(
227238 *
228239 * @param featureType requested FT
229240 * @param attributes requested attributes
230- * @param filterCQL requested filter
241+ * @param filter requested filter
231242 */
232- private void validateExcelLimits (TMFeatureType featureType , Set <String > attributes , String filterCQL ) {
243+ private void validateExcelLimits (TMFeatureType featureType , Set <String > attributes , @ Nullable Filter filter ) {
233244 if (attributes .size () > ExcelDataStore .getMaxColumns ()) {
234245 throw new ResponseStatusException (
235246 HttpStatus .BAD_REQUEST ,
@@ -245,8 +256,7 @@ private void validateExcelLimits(TMFeatureType featureType, Set<String> attribut
245256 q .setPropertyNames (attributes .toArray (new String [0 ]));
246257 }
247258
248- if (!StringUtils .isBlank (filterCQL )) {
249- Filter filter = ECQL .toFilter (filterCQL );
259+ if (filter != null ) {
250260 q .setFilter (filter );
251261 }
252262 final int featCount = inputFeatureSource .getCount (q );
@@ -255,10 +265,12 @@ private void validateExcelLimits(TMFeatureType featureType, Set<String> attribut
255265 HttpStatus .BAD_REQUEST ,
256266 "Excel format does not support more than " + ExcelDataStore .getMaxRows () + " rows" );
257267 }
258- } catch (CQLException | IOException e ) {
268+ } catch (IOException e ) {
259269 throw new ResponseStatusException (
260270 HttpStatus .INTERNAL_SERVER_ERROR ,
261271 "Failed to count all features for Excel extract: " + e .getMessage ());
272+ } catch (IllegalArgumentException e ) {
273+ throw new ResponseStatusException (HttpStatus .BAD_REQUEST , "Invalid filter" );
262274 } finally {
263275 if (inputFeatureSource != null ) {
264276 inputFeatureSource .getDataStore ().dispose ();
0 commit comments