Browse Source

Merge branch 'rules' of https://github.com/fosrl/pangolin into rules

Milo Schwartz 5 months ago
parent
commit
5b44ffa2fb

+ 39 - 14
server/routers/badger/verifySession.ts

@@ -485,18 +485,42 @@ async function checkRules(
         return;
     }
 
+    let hasAcceptRule = false;
+
+    // First pass: look for DROP rules
     for (const rule of rules) {
         if (
-            clientIp &&
-            rule.match == "CIDR" &&
-            isIpInCidr(clientIp, rule.value)
+            (clientIp &&
+                rule.match == "CIDR" &&
+                isIpInCidr(clientIp, rule.value) &&
+                rule.action === "DROP") ||
+            (path &&
+                rule.match == "PATH" &&
+                urlGlobToRegex(rule.value).test(path) &&
+                rule.action === "DROP")
         ) {
-            return rule.action as "ACCEPT" | "DROP";
-        } else if (path && rule.match == "PATH") {
-            // rule.value is a regex, match on the path and see if it matches
-            const re = urlGlobToRegex(rule.value);
-            if (re.test(path)) {
-                return rule.action as "ACCEPT" | "DROP";
+            return "DROP";
+        }
+        // Track if we see any ACCEPT rules for the second pass
+        if (rule.action === "ACCEPT") {
+            hasAcceptRule = true;
+        }
+    }
+
+    // Second pass: only check ACCEPT rules if we found one and didn't find a DROP
+    if (hasAcceptRule) {
+        for (const rule of rules) {
+            if (rule.action !== "ACCEPT") continue;
+
+            if (
+                (clientIp &&
+                    rule.match == "CIDR" &&
+                    isIpInCidr(clientIp, rule.value)) ||
+                (path &&
+                    rule.match == "PATH" &&
+                    urlGlobToRegex(rule.value).test(path))
+            ) {
+                return "ACCEPT";
             }
         }
     }
@@ -505,8 +529,8 @@ async function checkRules(
 }
 
 function urlGlobToRegex(pattern: string): RegExp {
-    // Remove leading slash if present (we'll add it to the regex pattern)
-    pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern;
+    // Trim any leading or trailing slashes
+    pattern = pattern.replace(/^\/+|\/+$/g, "");
 
     // Escape special regex characters except *
     const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
@@ -516,6 +540,7 @@ function urlGlobToRegex(pattern: string): RegExp {
 
     // Create the final pattern that:
     // 1. Optionally matches leading slash
-    // 2. Matches the entire string
-    return new RegExp(`^/?${regexPattern}$`);
-}
+    // 2. Matches the pattern
+    // 3. Optionally matches trailing slash
+    return new RegExp(`^/?${regexPattern}/?$`);
+}

+ 2 - 2
src/app/[orgId]/settings/resources/[resourceId]/connectivity/page.tsx

@@ -269,7 +269,7 @@ export default function ReverseProxyTargets(props: {
                     >(`/resource/${params.resourceId}/target`, data);
                     target.targetId = res.data.data.targetId;
                 } else if (target.updated) {
-                    const res = await api.post(
+                    await api.post(
                         `/target/${target.targetId}`,
                         data
                     );
@@ -290,7 +290,7 @@ export default function ReverseProxyTargets(props: {
             for (const targetId of targetsToRemove) {
                 await api.delete(`/target/${targetId}`);
                 setTargets(
-                    targets.filter((target) => target.targetId !== targetId)
+                    targets.filter((t) => t.targetId !== targetId)
                 );
             }
 

+ 18 - 6
src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx

@@ -257,30 +257,42 @@ export default function ResourceRules(props: {
                 }
 
                 if (rule.new) {
-                    await api.put(`/resource/${params.resourceId}/rule`, data);
+                    const res = await api.put(`/resource/${params.resourceId}/rule`, data);
+                    rule.ruleId = res.data.data.ruleId;
                 } else if (rule.updated) {
                     await api.post(
                         `/resource/${params.resourceId}/rule/${rule.ruleId}`,
                         data
                     );
                 }
+
+                setRules([
+                    ...rules.map((r) => {
+                        let res = {
+                            ...r,
+                            new: false,
+                            updated: false
+                        };
+                        return res;
+                    })
+                ]);
             }
 
             for (const ruleId of rulesToRemove) {
                 await api.delete(
                     `/resource/${params.resourceId}/rule/${ruleId}`
                 );
+                setRules(
+                    rules.filter((r) => r.ruleId !== ruleId)
+                );
             }
 
-            setRules(
-                rules.map((rule) => ({ ...rule, new: false, updated: false }))
-            );
-            setRulesToRemove([]);
-
             toast({
                 title: "Rules updated",
                 description: "Rules updated successfully"
             });
+
+            setRulesToRemove([]);
         } catch (err) {
             console.error(err);
             toast({