homelab: stop sops-policy scan resetting state on the next rule

Critical bug — the loop reset age_block_last_idx unconditionally on
every '- path_regex:' line. So after finding the target rule's age
block, encountering the NEXT rule wiped the result and the function
returned False. _grant_shared_secrets / _revoke_shared_secrets both
silently no-op'd because of this.

Fix: break out of the scan once we've collected the target rule's
data. Refactored the remove helper to share a find_target_age_lines
inner so the dangling-trailing-comma fixup uses the same logic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-20 19:29:20 +02:00
parent 098d4cfd6e
commit 5a8d6cfd4f

View File

@@ -141,6 +141,10 @@ def _add_recipient_to_sops_policy(sops_path: Path, path_regex_pattern: str, pubk
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("- path_regex:"):
# If we've already collected what we need from the target rule,
# stop — don't let the next rule wipe age_block_last_idx.
if in_target_rule and age_block_last_idx is not None:
break
in_target_rule = path_regex_pattern in line
age_block_start = None
age_block_last_idx = None
@@ -152,13 +156,12 @@ def _add_recipient_to_sops_policy(sops_path: Path, path_regex_pattern: str, pubk
continue
if age_block_start is None:
continue
# Inside the age block; track the last age1... line.
if "age1" in stripped:
if pubkey in line:
return True # already a recipient
age_block_last_idx = i
elif stripped == "" or stripped.startswith("#"):
continue # blank / comment inside the block
continue
else:
break # next key, age block ended
if age_block_last_idx is None:
@@ -204,59 +207,47 @@ def _remove_recipient_from_sops_policy(sops_path: Path, path_regex_pattern: str,
if not sops_path.exists():
return False
lines = sops_path.read_text().splitlines(keepends=True)
in_target_rule = False
age_block_start = None
target_idx = None
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("- path_regex:"):
in_target_rule = path_regex_pattern in line
age_block_start = None
target_idx = None
continue
if not in_target_rule:
continue
if "age: >-" in line:
age_block_start = i
continue
if age_block_start is None:
continue
if "age1" in stripped:
if pubkey in line:
target_idx = i
def find_target_age_lines() -> tuple[list[int], bool]:
"""Scan and return (indices_of_age1_lines_in_target_rule, found_rule)."""
in_target = False
age_block_start = None
age_idxs: list[int] = []
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("- path_regex:"):
if in_target and age_idxs:
return age_idxs, True
in_target = path_regex_pattern in line
age_block_start = None
age_idxs = []
continue
if not in_target:
continue
if "age: >-" in line:
age_block_start = i
continue
if age_block_start is None:
continue
if "age1" in stripped:
age_idxs.append(i)
elif stripped == "" or stripped.startswith("#"):
continue
else:
break
elif stripped == "" or stripped.startswith("#"):
continue
else:
break # next key, age block ended
return age_idxs, in_target
age_idxs, found = find_target_age_lines()
if not found:
return False
target_idx = next((i for i in age_idxs if pubkey in lines[i]), None)
if target_idx is None:
return True # nothing to remove — already absent
return True # already absent
del lines[target_idx]
# Fix a now-dangling trailing comma on the new last age line if any.
# Find the new last age line in this rule's age block.
in_target_rule = False
age_block_start = None
last_age_idx = None
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("- path_regex:"):
in_target_rule = path_regex_pattern in line
age_block_start = None
continue
if not in_target_rule:
continue
if "age: >-" in line:
age_block_start = i
continue
if age_block_start is None:
continue
if "age1" in stripped:
last_age_idx = i
elif stripped == "" or stripped.startswith("#"):
continue
else:
break
if last_age_idx is not None:
# After deletion, fix trailing comma on the new last age line.
new_age_idxs, _ = find_target_age_lines()
if new_age_idxs:
last_age_idx = new_age_idxs[-1]
last_line = lines[last_age_idx]
if last_line.rstrip().endswith(","):
lines[last_age_idx] = last_line.rstrip().rstrip(",") + "\n"