Owen 5 mēneši atpakaļ
vecāks
revīzija
42434ca832

+ 17 - 1
server/routers/badger/verifySession.ts

@@ -480,7 +480,7 @@ async function checkRules(
             return rule.action == "ACCEPT"; 
         } else if (path && rule.match == "PATH") {
             // rule.value is a regex, match on the path and see if it matches
-            const re = new RegExp(rule.value);
+            const re = urlGlobToRegex(rule.value);
             if (re.test(path)) {
                 return rule.action == "ACCEPT";
             }
@@ -489,3 +489,19 @@ async function checkRules(
 
     return false;
 }
+
+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;
+  
+    // Escape special regex characters except *
+    const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
+  
+    // Replace * with regex pattern for any valid URL segment characters
+    const regexPattern = escapedPattern.replace(/\*/g, '[a-zA-Z0-9_-]+');
+  
+    // Create the final pattern that:
+    // 1. Optionally matches leading slash
+    // 2. Matches the entire string
+    return new RegExp(`^/?${regexPattern}$`);
+  }

+ 121 - 21
src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx

@@ -93,9 +93,9 @@ export default function ResourceRules(props: {
     useEffect(() => {
         const fetchRules = async () => {
             try {
-                const res = await api.get<AxiosResponse<ListResourceRulesResponse>>(
-                    `/resource/${params.resourceId}/rules`
-                );
+                const res = await api.get<
+                    AxiosResponse<ListResourceRulesResponse>
+                >(`/resource/${params.resourceId}/rules`);
                 if (res.status === 200) {
                     setRules(res.data.data.rules);
                 }
@@ -133,6 +133,25 @@ export default function ResourceRules(props: {
             return;
         }
 
+        if (data.match === "CIDR" && !isValidCIDR(data.value)) {
+            toast({
+                variant: "destructive",
+                title: "Invalid CIDR",
+                description: "Please enter a valid CIDR value"
+            });
+            setLoading(false);
+            return;
+        }
+        if (data.match === "PATH" && !isValidUrlGlobPattern(data.value)) {
+            toast({
+                variant: "destructive",
+                title: "Invalid URL path",
+                description: "Please enter a valid URL path value"
+            });
+            setLoading(false);
+            return;
+        }
+
         const newRule: LocalRule = {
             ...data,
             ruleId: new Date().getTime(),
@@ -171,11 +190,27 @@ export default function ResourceRules(props: {
                     value: rule.value
                 };
 
+                if (rule.match === "CIDR" && !isValidCIDR(rule.value)) {
+                    toast({
+                        variant: "destructive",
+                        title: "Invalid CIDR",
+                        description: "Please enter a valid CIDR value"
+                    });
+                    setLoading(false);
+                    return;
+                }
+                if (rule.match === "PATH" && !isValidUrlGlobPattern(rule.value)) {
+                    toast({
+                        variant: "destructive",
+                        title: "Invalid URL path",
+                        description: "Please enter a valid URL path value"
+                    });
+                    setLoading(false);
+                    return;
+                }
+
                 if (rule.new) {
-                    await api.put(
-                        `/resource/${params.resourceId}/rule`,
-                        data
-                    );
+                    await api.put(`/resource/${params.resourceId}/rule`, data);
                 } else if (rule.updated) {
                     await api.post(
                         `/resource/${params.resourceId}/rule/${rule.ruleId}`,
@@ -190,7 +225,9 @@ export default function ResourceRules(props: {
                 );
             }
 
-            setRules(rules.map(rule => ({ ...rule, new: false, updated: false })));
+            setRules(
+                rules.map((rule) => ({ ...rule, new: false, updated: false }))
+            );
             setRulesToRemove([]);
 
             toast({
@@ -322,7 +359,9 @@ export default function ResourceRules(props: {
                                             <FormControl>
                                                 <Select
                                                     value={field.value}
-                                                    onValueChange={field.onChange}
+                                                    onValueChange={
+                                                        field.onChange
+                                                    }
                                                 >
                                                     <SelectTrigger>
                                                         <SelectValue />
@@ -350,7 +389,9 @@ export default function ResourceRules(props: {
                                             <FormControl>
                                                 <Select
                                                     value={field.value}
-                                                    onValueChange={field.onChange}
+                                                    onValueChange={
+                                                        field.onChange
+                                                    }
                                                 >
                                                     <SelectTrigger>
                                                         <SelectValue />
@@ -380,7 +421,8 @@ export default function ResourceRules(props: {
                                             </FormControl>
                                             <FormMessage />
                                             <FormDescription>
-                                                Enter CIDR or path value based on match type
+                                                Enter CIDR or path value based
+                                                on match type
                                             </FormDescription>
                                         </FormItem>
                                     )}
@@ -401,7 +443,8 @@ export default function ResourceRules(props: {
                                                 {header.isPlaceholder
                                                     ? null
                                                     : flexRender(
-                                                          header.column.columnDef.header,
+                                                          header.column
+                                                              .columnDef.header,
                                                           header.getContext()
                                                       )}
                                             </TableHead>
@@ -413,14 +456,17 @@ export default function ResourceRules(props: {
                                 {table.getRowModel().rows?.length ? (
                                     table.getRowModel().rows.map((row) => (
                                         <TableRow key={row.id}>
-                                            {row.getVisibleCells().map((cell) => (
-                                                <TableCell key={cell.id}>
-                                                    {flexRender(
-                                                        cell.column.columnDef.cell,
-                                                        cell.getContext()
-                                                    )}
-                                                </TableCell>
-                                            ))}
+                                            {row
+                                                .getVisibleCells()
+                                                .map((cell) => (
+                                                    <TableCell key={cell.id}>
+                                                        {flexRender(
+                                                            cell.column
+                                                                .columnDef.cell,
+                                                            cell.getContext()
+                                                        )}
+                                                    </TableCell>
+                                                ))}
                                         </TableRow>
                                     ))
                                 ) : (
@@ -449,4 +495,58 @@ export default function ResourceRules(props: {
             </SettingsSection>
         </SettingsContainer>
     );
-}
+}
+
+function isValidCIDR(cidr: string): boolean {
+    // Match CIDR pattern (e.g., "192.168.0.0/24")
+    const cidrPattern =
+        /^([0-9]{1,3}\.){3}[0-9]{1,3}\/([0-9]|[1-2][0-9]|3[0-2])$/;
+
+    if (!cidrPattern.test(cidr)) {
+        return false;
+    }
+
+    // Validate IP address part
+    const ipPart = cidr.split("/")[0];
+    const octets = ipPart.split(".");
+
+    return octets.every((octet) => {
+        const num = parseInt(octet, 10);
+        return num >= 0 && num <= 255;
+    });
+}
+
+function isValidUrlGlobPattern(pattern: string): boolean {
+    // Remove leading slash if present
+    pattern = pattern.startsWith('/') ? pattern.slice(1) : pattern;
+    
+    // Empty string is not valid
+    if (!pattern) {
+      return false;
+    }
+  
+    // Split path into segments
+    const segments = pattern.split('/');
+    
+    // Check each segment
+    for (let i = 0; i < segments.length; i++) {
+      const segment = segments[i];
+      
+      // Empty segments are not allowed (double slashes)
+      if (!segment && i !== segments.length - 1) {
+        return false;
+      }
+      
+      // If segment contains *, it must be exactly *
+      if (segment.includes('*') && segment !== '*') {
+        return false;
+      }
+      
+      // Check for invalid characters
+      if (!/^[a-zA-Z0-9_*-]*$/.test(segment)) {
+        return false;
+      }
+    }
+    
+    return true;
+  }