From 383e4b5c6ec7d12c194571729f32cc88d2ac3699 Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Sat, 30 May 2026 05:42:53 -0700 Subject: [PATCH] release: archivebox 0.9.33rc45 --- archivebox/api/v1_crawls.py | 8 + archivebox/api/v1_personas.py | 8 + archivebox/base_models/admin.py | 55 +++++- archivebox/cli/archivebox_server.py | 44 +++-- archivebox/config/collection.py | 259 ++++++++++++++++++++++++---- archivebox/config/common.py | 41 +++++ archivebox/core/forms.py | 14 +- archivebox/core/views.py | 13 +- archivebox/crawls/models.py | 3 +- archivebox/machine/admin.py | 44 ++++- archivebox/machine/models.py | 42 ++++- etc/package.json | 2 +- pyproject.toml | 8 +- 13 files changed, 486 insertions(+), 55 deletions(-) diff --git a/archivebox/api/v1_crawls.py b/archivebox/api/v1_crawls.py index 11fef6d7..32c1006b 100644 --- a/archivebox/api/v1_crawls.py +++ b/archivebox/api/v1_crawls.py @@ -61,6 +61,14 @@ class CrawlSchema(Schema): username = getattr(user, "username", None) return username if isinstance(username, str) else str(user) + @staticmethod + def resolve_config(obj): + # Redact credential values so REST responses can never leak the raw + # token/secret/api-key that the operator stored in Crawl.config. + from archivebox.config.common import redact_sensitive_config + + return redact_sensitive_config(obj.config) + @staticmethod def resolve_snapshots(obj, context): if bool(getattr(context["request"], "with_snapshots", False)): diff --git a/archivebox/api/v1_personas.py b/archivebox/api/v1_personas.py index c0a19cdb..24e98117 100644 --- a/archivebox/api/v1_personas.py +++ b/archivebox/api/v1_personas.py @@ -51,6 +51,14 @@ class PersonaSchema(Schema): def resolve_created_by_username(obj) -> str: return obj.created_by.username + @staticmethod + def resolve_config(obj): + # Redact credential values so REST responses don't leak the raw + # token/secret/api-key the operator stored in Persona.config. + from archivebox.config.common import redact_sensitive_config + + return redact_sensitive_config(obj.config) + class PersonaSyncResponseSchema(Schema): success: bool diff --git a/archivebox/base_models/admin.py b/archivebox/base_models/admin.py index 606dd7b1..f1c06577 100644 --- a/archivebox/base_models/admin.py +++ b/archivebox/base_models/admin.py @@ -722,12 +722,34 @@ class KeyValueWidget(forms.Widget): return mark_safe(html) def _render_row(self, widget_id: str, key: str, value: str) -> str: + from archivebox.config.common import is_sensitive_config_key + + # Sensitive keys (``*TOKEN*``, ``*SECRET*``, ``*API_KEY*``, ``*APIKEY*``) are + # rendered write-only: the input is a password field with a placeholder + # showing the value is set, but the raw value is NEVER sent to the browser. + # When the user submits the form with the field left blank, the + # ``ConfigEditorMixin.save_model`` hook re-merges the previously-saved + # value so leaving it untouched is a no-op rather than a destructive clear. + is_sensitive = is_sensitive_config_key(key) + has_value = bool(value) + if is_sensitive: + input_type = "password" + rendered_value = "" + placeholder = ( + "•••••• (saved — enter new value to replace, clear by deleting row)" if has_value else "value (will be saved write-only)" + ) + extra_attrs = ' autocomplete="off" data-sensitive="1"' + (' data-had-value="1"' if has_value else "") + else: + input_type = "text" + rendered_value = self._escape(value) + placeholder = "value" + extra_attrs = "" return f'''
-