|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import re |
3 | 4 | import uuid |
4 | 5 | from ast import literal_eval |
5 | 6 | from datetime import timedelta |
@@ -68,14 +69,61 @@ def has_api_terms(word: str): |
68 | 69 | return "api" in word and ("key" in word or ("token" in word and "tokens" not in word)) |
69 | 70 |
|
70 | 71 |
|
| 72 | +def _get_provider_from_template(template: dict) -> str | None: |
| 73 | + """Return provider name from template's model field, if any.""" |
| 74 | + model_field = template.get("model") |
| 75 | + if not isinstance(model_field, dict): |
| 76 | + return None |
| 77 | + raw = model_field.get("value") |
| 78 | + if isinstance(raw, list) and len(raw) > 0 and isinstance(raw[0], dict): |
| 79 | + return raw[0].get("provider") |
| 80 | + return None |
| 81 | + |
| 82 | + |
| 83 | +def _looks_like_variable_name(value: Any) -> bool: |
| 84 | + """Return True if value looks like a variable name.""" |
| 85 | + if not value or not isinstance(value, str) or not value.strip(): |
| 86 | + return False |
| 87 | + return bool(re.fullmatch(r"[A-Za-z][A-Za-z0-9_]*", value.strip())) |
| 88 | + |
| 89 | + |
| 90 | +def replace_api_key_with_env_var_name(flow: dict) -> dict: |
| 91 | + """Normalize api_key to a variable name when possible, never export raw keys.""" |
| 92 | + for node in flow.get("data", {}).get("nodes", []): |
| 93 | + node_data = node.get("data") |
| 94 | + if not isinstance(node_data, dict): |
| 95 | + continue |
| 96 | + node_inner = node_data.get("node") |
| 97 | + if not isinstance(node_inner, dict): |
| 98 | + continue |
| 99 | + template = node_inner.get("template") |
| 100 | + if not isinstance(template, dict): |
| 101 | + continue |
| 102 | + for value in template.values(): |
| 103 | + if ( |
| 104 | + isinstance(value, dict) |
| 105 | + and value.get("name") == "api_key" |
| 106 | + and value.get("password") |
| 107 | + ): |
| 108 | + current = value.get("value") |
| 109 | + if _looks_like_variable_name(current): |
| 110 | + break # keep user's custom variable name |
| 111 | + # raw secret or other string: clear it |
| 112 | + value["value"] = None |
| 113 | + break |
| 114 | + return flow |
| 115 | + |
| 116 | + |
71 | 117 | def remove_api_keys(flow: dict): |
72 | | - """Remove api keys from flow data.""" |
| 118 | + """Clear secret values from flow data.""" |
| 119 | + flow = replace_api_key_with_env_var_name(flow) |
73 | 120 | for node in flow.get("data", {}).get("nodes", []): |
74 | 121 | node_data = node.get("data").get("node") |
75 | 122 | template = node_data.get("template") |
76 | 123 | for value in template.values(): |
77 | 124 | if isinstance(value, dict) and "name" in value and has_api_terms(value["name"]) and value.get("password"): |
78 | | - value["value"] = None |
| 125 | + if value.get("name") != "api_key": |
| 126 | + value["value"] = None |
79 | 127 |
|
80 | 128 | return flow |
81 | 129 |
|
|
0 commit comments