test123
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s

This commit is contained in:
2026-01-11 17:12:19 +01:00
parent c33c529fbb
commit 57de326910

View File

@@ -202,19 +202,6 @@ def ensure_nft_available():
def nft_list_rules(table: str = "filter", chain: str = "forward") -> Dict[str, Any]: def nft_list_rules(table: str = "filter", chain: str = "forward") -> Dict[str, Any]:
"""Return nft rules parsed from `nft --json list ruleset`.
For each rule we return a dict with at least:
- family, table, chain
- handle (if present)
- position (1-based index within chain)
- comment (if present in expr)
- verdict (best-effort string like 'accept'/'drop'/'reject')
- raw_exprs: the original expr list from nft JSON
This makes it easier for the frontend to show position and edit
or replace a specific rule by handle/position.
"""
ensure_nft_available() ensure_nft_available()
cmd = [NFT_BIN, "--json", "list", "ruleset"] cmd = [NFT_BIN, "--json", "list", "ruleset"]
try: try:
@@ -224,69 +211,88 @@ def nft_list_rules(table: str = "filter", chain: str = "forward") -> Dict[str, A
raise HTTPException(status_code=500, detail=f"nft failed: {e.stderr.decode()}") raise HTTPException(status_code=500, detail=f"nft failed: {e.stderr.decode()}")
results: List[Dict[str, Any]] = [] results: List[Dict[str, Any]] = []
# counters per chain to compute position
counters: Dict[str, int] = {} counters: Dict[str, int] = {}
# The JSON usually contains a top-level dict with key 'nftables' -> list
items = parsed.get("nftables") if isinstance(parsed, dict) else parsed items = parsed.get("nftables") if isinstance(parsed, dict) else parsed
if not isinstance(items, list): if not isinstance(items, list):
items = [] items = []
for item in items: for item in items:
# Keep a simple mapping of current table/chain context # skip table/chain metadata entries (we only need rule entries)
if 'table' in item: if "rule" not in item:
tbl = item['table']
# nothing to do for context; continue
continue continue
if 'chain' in item:
ch = item['chain'] r = item["rule"]
# continue; chain metadata present family = r.get("family")
continue table_name = r.get("table")
if 'rule' in item: chain_name = r.get("chain")
r = item['rule']
family = r.get('family')
table_name = r.get('table')
chain_name = r.get('chain')
key = f"{family}:{table_name}:{chain_name}" key = f"{family}:{table_name}:{chain_name}"
counters.setdefault(key, 0) counters.setdefault(key, 0)
counters[key] += 1 counters[key] += 1
position = counters[key] position = counters[key]
handle = r.get('handle') handle = r.get("handle")
# extract comment and verdict best-effort
raw_exprs = r.get('expr', []) exprs = r.get("expr", []) # original expression list from nft JSON
comment = None comment = None
verdict = None verdict = None
for expr in raw_exprs: verdict_details = None
if isinstance(expr, dict):
if 'comment' in expr: # scan expressions to extract comment and verdict/action
comment = expr.get('comment') for expr in exprs:
if 'verdict' in expr: if not isinstance(expr, dict):
# verdict might be dict like {'verdict': {'kind': 'accept'}} continue
v = expr['verdict'] # comment can appear as {"comment":"text"} or {"comment": {"text": "..."}} depending on nft json variant
if "comment" in expr:
# handle both simple and nested forms
c = expr.get("comment")
if isinstance(c, str):
comment = c
elif isinstance(c, dict):
# some representations: {"comment": {"text": "..."}} or {"comment": {"str": "..."}}
comment = c.get("text") or c.get("str") or comment
# verdict forms
if "verdict" in expr:
v = expr["verdict"]
# v often looks like {"accept": None} or {"drop": None} or {"reject": {...}}
if isinstance(v, dict): if isinstance(v, dict):
verdict = list(v.keys())[0] # take the first key as the action
k = next(iter(v.keys()), None)
verdict = k
verdict_details = v.get(k)
else: else:
verdict = str(v) verdict = str(v)
# some JSON formats have 'match' or 'payload' etc. look for 'type' fields # older/alternate forms
if 'reject' in expr: if "drop" in expr and verdict is None:
verdict = 'reject' verdict = "drop"
verdict_details = expr.get("drop")
if "accept" in expr and verdict is None:
verdict = "accept"
verdict_details = expr.get("accept")
if "reject" in expr and verdict is None:
verdict = "reject"
verdict_details = expr.get("reject")
results.append({ results.append({
'family': family, "family": family,
'table': table_name, "table": table_name,
'chain': chain_name, "chain": chain_name,
'handle': handle, "handle": handle,
'position': position, "position": position,
'comment': comment, "comment": comment,
'verdict': verdict, "verdict": verdict, # e.g. "accept", "drop", "reject", or None
'raw_exprs': raw_exprs, "verdict_details": verdict_details, # raw details for rejects/other actions
'raw_rule': r, "exprs": exprs, # original expression list (renamed, clearer)
"nft_rule": r, # the original nft JSON dict for this rule
}) })
# Optionally filter by table/chain if requested
# filter by requested table/chain if provided
if table or chain: if table or chain:
results = [x for x in results if x['table'] == table and x['chain'] == chain] results = [x for x in results if x["table"] == table and x["chain"] == chain]
return {"rules": results} return {"rules": results}
def expr_to_nft_snippet(e: Expr) -> str: def expr_to_nft_snippet(e: Expr) -> str:
# runtime dispatch via model type # runtime dispatch via model type
if isinstance(e, MetaExpr): if isinstance(e, MetaExpr):