priority best guess fix
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
This commit is contained in:
@@ -263,6 +263,45 @@ class RulesetOut(BaseModel):
|
|||||||
_handle_re = re.compile(r"\s+#\s*handle\s+\d+\s*$")
|
_handle_re = re.compile(r"\s+#\s*handle\s+\d+\s*$")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_priority(val: Any) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
Robustly parse a priority value returned in various nft JSON shapes.
|
||||||
|
Accepts:
|
||||||
|
- int -> returns unchanged
|
||||||
|
- numeric string -> parsed int
|
||||||
|
- dict -> tries common nested keys ('priority', 'prio')
|
||||||
|
Returns None if not parseable.
|
||||||
|
"""
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
|
# if it's already an int
|
||||||
|
if isinstance(val, int):
|
||||||
|
return val
|
||||||
|
# numeric string
|
||||||
|
if isinstance(val, str):
|
||||||
|
s = val.strip()
|
||||||
|
if s.isdigit() or (s.startswith("-") and s[1:].isdigit()):
|
||||||
|
try:
|
||||||
|
return int(s)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
# sometimes nft uses "0" etc with whitespace
|
||||||
|
try:
|
||||||
|
return int(float(s))
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
# nested dicts sometimes appear
|
||||||
|
if isinstance(val, dict):
|
||||||
|
for key in ("priority", "prio"):
|
||||||
|
if key in val:
|
||||||
|
return parse_priority(val.get(key))
|
||||||
|
# sometimes structure like {'hook': {'priority': 0}} - try to dive in
|
||||||
|
for v in val.values():
|
||||||
|
p = parse_priority(v)
|
||||||
|
if p is not None:
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
def rule_text_from_expr(expr: Any) -> str:
|
def rule_text_from_expr(expr: Any) -> str:
|
||||||
"""
|
"""
|
||||||
Deterministic serializer to produce a compact UI-friendly string from expr list.
|
Deterministic serializer to produce a compact UI-friendly string from expr list.
|
||||||
@@ -347,28 +386,40 @@ def build_predictable_ruleset(nft_json: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
cname = ch.get("name")
|
cname = ch.get("name")
|
||||||
if fam and table_name and cname:
|
if fam and table_name and cname:
|
||||||
tables.setdefault((fam, table_name), {"family": fam, "name": table_name, "chains": {}})
|
tables.setdefault((fam, table_name), {"family": fam, "name": table_name, "chains": {}})
|
||||||
# create chain with metadata fields (if present)
|
chains_map = tables[(fam, table_name)]["chains"]
|
||||||
chain_obj = tables[(fam, table_name)]["chains"].setdefault(
|
|
||||||
cname,
|
# existing chain (maybe created earlier by rule processing)
|
||||||
{
|
existing = chains_map.get(cname)
|
||||||
|
# extract metadata robustly
|
||||||
|
ch_type = ch.get("type")
|
||||||
|
ch_hook = ch.get("hook")
|
||||||
|
ch_priority = parse_priority(ch.get("priority") if "priority" in ch else ch.get("prio") if "prio" in ch else ch.get("prio", None))
|
||||||
|
# also attempt to parse nested shapes if present (some nft JSON variations)
|
||||||
|
if ch_priority is None:
|
||||||
|
ch_priority = parse_priority(ch.get("hook") if isinstance(ch.get("hook"), dict) else None)
|
||||||
|
|
||||||
|
ch_policy = ch.get("policy")
|
||||||
|
|
||||||
|
if existing is None:
|
||||||
|
chains_map[cname] = {
|
||||||
"name": cname,
|
"name": cname,
|
||||||
"type": ch.get("type"),
|
"type": ch_type,
|
||||||
"hook": ch.get("hook"),
|
"hook": ch_hook,
|
||||||
"priority": ch.get("priority"),
|
"priority": ch_priority,
|
||||||
"policy": ch.get("policy"),
|
"policy": ch_policy,
|
||||||
"rules": [],
|
"rules": [],
|
||||||
},
|
}
|
||||||
)
|
else:
|
||||||
# if the chain already existed (due to earlier rules), ensure we add missing metadata if present
|
# merge into placeholder (do not overwrite existing rules)
|
||||||
if isinstance(chain_obj, dict):
|
if isinstance(existing, dict):
|
||||||
if chain_obj.get("type") is None and ch.get("type") is not None:
|
if existing.get("type") is None and ch_type is not None:
|
||||||
chain_obj["type"] = ch.get("type")
|
existing["type"] = ch_type
|
||||||
if chain_obj.get("hook") is None and ch.get("hook") is not None:
|
if existing.get("hook") is None and ch_hook is not None:
|
||||||
chain_obj["hook"] = ch.get("hook")
|
existing["hook"] = ch_hook
|
||||||
if chain_obj.get("priority") is None and ch.get("priority") is not None:
|
if existing.get("priority") is None and ch_priority is not None:
|
||||||
chain_obj["priority"] = ch.get("priority")
|
existing["priority"] = ch_priority
|
||||||
if chain_obj.get("policy") is None and ch.get("policy") is not None:
|
if existing.get("policy") is None and ch_policy is not None:
|
||||||
chain_obj["policy"] = ch.get("policy")
|
existing["policy"] = ch_policy
|
||||||
# rule records
|
# rule records
|
||||||
elif "rule" in rec:
|
elif "rule" in rec:
|
||||||
r = rec["rule"]
|
r = rec["rule"]
|
||||||
@@ -379,11 +430,10 @@ def build_predictable_ruleset(nft_json: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
expr = r.get("expr")
|
expr = r.get("expr")
|
||||||
if fam and table_name and chain_name:
|
if fam and table_name and chain_name:
|
||||||
tables.setdefault((fam, table_name), {"family": fam, "name": table_name, "chains": {}})
|
tables.setdefault((fam, table_name), {"family": fam, "name": table_name, "chains": {}})
|
||||||
# ensure chain record exists, preserve placeholders for metadata if not yet set
|
chains_map = tables[(fam, table_name)]["chains"]
|
||||||
tables[(fam, table_name)]["chains"].setdefault(
|
# ensure chain placeholder exists, with possible metadata defaults
|
||||||
chain_name,
|
chains_map.setdefault(chain_name, {"name": chain_name, "type": None, "hook": None, "priority": None, "policy": None, "rules": []})
|
||||||
{"name": chain_name, "type": None, "hook": None, "priority": None, "policy": None, "rules": []},
|
|
||||||
)
|
|
||||||
rule_obj: Dict[str, Any] = {
|
rule_obj: Dict[str, Any] = {
|
||||||
"handle": handle,
|
"handle": handle,
|
||||||
"expr": expr,
|
"expr": expr,
|
||||||
@@ -394,7 +444,25 @@ def build_predictable_ruleset(nft_json: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
rule_obj["position"] = r["position"]
|
rule_obj["position"] = r["position"]
|
||||||
if "comment" in r:
|
if "comment" in r:
|
||||||
rule_obj["comment"] = r["comment"]
|
rule_obj["comment"] = r["comment"]
|
||||||
tables[(fam, table_name)]["chains"][chain_name]["rules"].append(rule_obj)
|
chains_map[chain_name]["rules"].append(rule_obj)
|
||||||
|
|
||||||
|
# Attempt to salvage chain metadata from rule record if present
|
||||||
|
# some nft JSON may include 'chain' subfields inside rule record
|
||||||
|
# e.g. r.get('chain') might be an object - handle that defensively
|
||||||
|
if isinstance(r.get("chain"), dict):
|
||||||
|
csub = r.get("chain")
|
||||||
|
# try to parse nested priority
|
||||||
|
if chains_map[chain_name].get("priority") is None:
|
||||||
|
parsed_prio = parse_priority(csub.get("priority") if "priority" in csub else csub.get("prio"))
|
||||||
|
if parsed_prio is not None:
|
||||||
|
chains_map[chain_name]["priority"] = parsed_prio
|
||||||
|
# type/hook/policy from nested if present
|
||||||
|
if chains_map[chain_name].get("type") is None and csub.get("type") is not None:
|
||||||
|
chains_map[chain_name]["type"] = csub.get("type")
|
||||||
|
if chains_map[chain_name].get("hook") is None and csub.get("hook") is not None:
|
||||||
|
chains_map[chain_name]["hook"] = csub.get("hook")
|
||||||
|
if chains_map[chain_name].get("policy") is None and csub.get("policy") is not None:
|
||||||
|
chains_map[chain_name]["policy"] = csub.get("policy")
|
||||||
|
|
||||||
# Convert map to sorted lists for deterministic order, and include chain metadata
|
# Convert map to sorted lists for deterministic order, and include chain metadata
|
||||||
for (fam, tname) in sorted(tables.keys(), key=lambda k: (k[0], k[1])):
|
for (fam, tname) in sorted(tables.keys(), key=lambda k: (k[0], k[1])):
|
||||||
|
|||||||
Reference in New Issue
Block a user