From 2648afef84620c5b239ac1c561bdcee487efa899 Mon Sep 17 00:00:00 2001 From: badra001 Date: Fri, 2 Jan 2026 12:00:31 -0500 Subject: [PATCH] update? --- .../cross-organization/audit_filter.py | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/local-app/python-tools/cross-organization/audit_filter.py b/local-app/python-tools/cross-organization/audit_filter.py index e2048fc2..7263f731 100755 --- a/local-app/python-tools/cross-organization/audit_filter.py +++ b/local-app/python-tools/cross-organization/audit_filter.py @@ -9,7 +9,7 @@ import glob # --- VERSIONING --- -__version__ = "1.1.0" +__version__ = "1.1.2" def get_nested(data, key_path): keys = key_path.split('.') @@ -20,12 +20,34 @@ def get_nested(data, key_path): return None return data +def str_to_bool(val): + """Converts common truthy/falsy strings to actual Booleans.""" + if isinstance(val, bool): + return val + normalized = str(val).lower() + if normalized in ('true', 'yes', 't', 'y', '1', 'on'): + return True + if normalized in ('false', 'no', 'f', 'n', '0', 'off'): + return False + return val + def check_condition(item, field, operator, value): actual_value = get_nested(item, field) + if actual_value is None: return operator == "!=" + + # TYPE-AWARE CONVERSION: + # If the filter value looks like a boolean, convert BOTH to booleans for comparison + target_value = str_to_bool(value) + if isinstance(target_value, bool): + compare_value = str_to_bool(actual_value) + if operator == "==": return compare_value == target_value + if operator == "!=": return compare_value != target_value + return False # Regex doesn't apply to pure booleans + + # Standard string comparison for everything else str_actual = str(actual_value) - if operator == "==": return str_actual == value elif operator == "!=": @@ -38,19 +60,20 @@ def check_condition(item, field, operator, value): return False def parse_filter_string(f_str): + # Regex split that handles operators match = re.split(r'(==|!=|~)', f_str) return match if len(match) == 3 else None def main(): - parser = argparse.ArgumentParser(description=f"Advanced Audit Filter v{__version__}") + parser = argparse.ArgumentParser(description=f"Boolean-Aware Audit Filter v{__version__}") parser.add_argument("--input", help="JSON file to filter") - parser.add_argument("--and", action="append", dest="and_filters", help="Must match ALL of these (AND logic)") - parser.add_argument("--or", action="append", dest="or_filters", help="Must match AT LEAST ONE of these (OR logic)") + parser.add_argument("--and", action="append", dest="and_filters", help="Match ALL") + parser.add_argument("--or", action="append", dest="or_filters", help="Match ANY") parser.add_argument("--csv", help="Output to CSV") parser.add_argument("--json", action="store_true", help="Output to JSON") args = parser.parse_args() - # Resolve input + # Resolve latest input file automatically input_file = args.input or max(glob.glob("audit_results.*.json"), key=os.path.getctime, default=None) if not input_file: print("Error: No input file found."); sys.exit(1) @@ -63,16 +86,13 @@ def main(): filtered_results = [] for record in data: - # Evaluate AND group (All must be True) + # Implicitly true if no filters provided and_passed = all(check_condition(record, f[0], f[1], f[2]) for f in and_list) - - # Evaluate OR group (At least one must be True, or group is empty) or_passed = any(check_condition(record, f[0], f[1], f[2]) for f in or_list) if or_list else True if and_passed and or_passed: filtered_results.append(record) - # Output handling... if not filtered_results: print("No matches found.") return @@ -80,8 +100,13 @@ def main(): if args.json: print(json.dumps(filtered_results, indent=2)) elif args.csv: - # (Standard CSV export logic here) - print(f"Saved to {args.csv}") + # Simple flattening for baseline CSV + with open(args.csv, 'w', newline='') as f: + headers = ["org_id", "account_id", "alias", "ou_path"] + writer = csv.DictWriter(f, fieldnames=headers, extrasaction='ignore') + writer.writeheader() + writer.writerows(filtered_results) + print(f"Results saved to {args.csv}") else: for r in filtered_results: print(f"{r.get('account_id')} | {r.get('alias','N/A'):<20} | {r.get('ou_path')}")