From a595fd7dd12fb21c816b101fd086b7ac267fa003 Mon Sep 17 00:00:00 2001
From: Christopher Cope <ccope@netgate.com>
Date: Thu, 7 Jul 2022 13:13:38 -0400
Subject: [PATCH] Fixed handling of single rule selected with multi-delete
 Issue #9887

---
 .../local/pfSense/include/www/firewall_nat.inc   | 14 +++++++++-----
 src/usr/local/www/firewall_rules.php             | 16 ++++++++++++----
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/usr/local/pfSense/include/www/firewall_nat.inc b/src/usr/local/pfSense/include/www/firewall_nat.inc
index 6046fb434e..6e64eeb54b 100644
--- a/src/usr/local/pfSense/include/www/firewall_nat.inc
+++ b/src/usr/local/pfSense/include/www/firewall_nat.inc
@@ -766,7 +766,6 @@ function deleteMultipleNATrules($post, $json = false) {
 	init_config_arr(array('nat', 'separator'));
 	$a_separators = &$config['nat']['separator'];
 
-	$first_idx = 0;
 	$num_deleted = 0;
 
 	foreach ($post['rule'] as $rulei) {
@@ -780,16 +779,21 @@ function deleteMultipleNATrules($post, $json = false) {
 
 		unset($a_nat[$rulei]);
 
-		// Capture first changed filter index for later separator shifting
-		if (!$first_idx) {
-			$first_idx = $rulei;
+		// Update the separators
+		// As rules are deleted, $ridx has to be decremented or separator position will break
+		if (count($post['rule']) == 1) { // Need special handling of single rule deletion
+			$ridx = $rulei;
+		} else {
+			$ridx = $rulei - $num_deleted;
 		}
 
+		$mvnrows = -1;
+		move_separators($a_separators, $ridx, $mvnrows);
+
 		$num_deleted++;
 	}
 
 	if ($num_deleted) {
-		move_separators($a_separators, $first_idx, -$num_deleted);
 		if (write_config("NAT: Rule deleted")) {
 			if ($json) {
 				filter_configure();
diff --git a/src/usr/local/www/firewall_rules.php b/src/usr/local/www/firewall_rules.php
index a4915df73d..72ac60929d 100644
--- a/src/usr/local/www/firewall_rules.php
+++ b/src/usr/local/www/firewall_rules.php
@@ -199,19 +199,27 @@ if (isset($_POST['del_x'])) {
 		init_config_arr(array('filter', 'separator', strtolower($if)));
 		$a_separators = &$config['filter']['separator'][strtolower($if)];
 
-		$first_idx = 0;		
 		$num_deleted = 0;
+
 		foreach ($_POST['rule'] as $rulei) {
 			delete_nat_association($a_filter[$rulei]['associated-rule-id']);
 			unset($a_filter[$rulei]);
 
-			// Capture first changed filter index for later separator shifting
-			if (!$first_idx) $first_idx = ifridx($if, $rulei);
+			// Update the separators
+			// As rules are deleted, $ridx has to be decremented or separator position will break
+			if (count($_POST['rule']) == 1) { // Need special handling of single rule deletion
+				$ridx = ifridx($if, $rulei);
+			} else {
+				$ridx = ifridx($if, $rulei) - $num_deleted + 1;
+			}
+
+			$mvnrows = -1;
+			move_separators($a_separators, $ridx, $mvnrows);
+
 			$num_deleted++;
 		}
 
 		if ($num_deleted) {
-			move_separators($a_separators, $first_idx, -$num_deleted);
 			if (write_config(gettext("Firewall: Rules - deleted selected firewall rules."))) {
 				mark_subsystem_dirty('filter');
 			}
-- 
2.25.1

