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

This commit is contained in:
Milo Schwartz 2025-02-09 23:24:09 -05:00
commit 5b44ffa2fb
No known key found for this signature in database
3 changed files with 59 additions and 22 deletions

View file

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

View file

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

View file

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