mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
fix(security): mask secret fields in API responses and extract helpers (#276)
* fix(security): mask secret fields in API responses and extract helpers GET /config/secrets returned raw API keys in plaintext to the browser. GET /plugins/config returned merged config including deep-merged secrets. POST /plugins/config could overwrite existing secrets with empty strings when the GET endpoint returned masked values that were sent back unchanged. Changes: - Add src/web_interface/secret_helpers.py with reusable functions: find_secret_fields, separate_secrets, mask_secret_fields, mask_all_secret_values, remove_empty_secrets - GET /config/secrets: mask all values with '••••••••' - GET /plugins/config: mask x-secret fields with '' - POST /plugins/config: filter empty-string secrets before saving - pages_v3: mask secrets before rendering plugin config templates - Remove three duplicated inline find_secret_fields/separate_secrets definitions in api_v3.py (replaced by single imported module) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): harden secret masking against CodeRabbit findings - Fail-closed: return 500 when schema unavailable instead of leaking secrets - Fix falsey masking: use `is not None and != ''` instead of truthiness check so values like 0 or False are still redacted - Add array-item secret support: recurse into `type: array` items schema to detect and mask secrets like accounts[].token - pages_v3: fail-closed when schema properties missing Addresses CodeRabbit findings on PR #276: - Critical: fail-closed bypass when schema_mgr/schema missing - Major: falsey values not masked (0, False leak through) - Major: pages_v3 fail-open when schema absent - Major: array-item secrets unsupported Co-Authored-By: 5ymb01 <5ymb01@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: 5ymb01 <5ymb01@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash,
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from src.web_interface.secret_helpers import mask_secret_fields
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -405,11 +406,17 @@ def _load_plugin_config_partial(plugin_id):
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load manifest for {plugin_id}: {e}")
|
||||
|
||||
# Mask secret fields before rendering template (fail closed — never leak secrets)
|
||||
schema_properties = schema.get('properties') if isinstance(schema, dict) else None
|
||||
if not isinstance(schema_properties, dict):
|
||||
return '<div class="text-red-500 p-4">Error loading plugin config securely: schema unavailable.</div>', 500
|
||||
config = mask_secret_fields(config, schema_properties)
|
||||
|
||||
# Determine enabled status
|
||||
enabled = config.get('enabled', True)
|
||||
if plugin_instance:
|
||||
enabled = plugin_instance.enabled
|
||||
|
||||
|
||||
# Build plugin data for template
|
||||
plugin_data = {
|
||||
'id': plugin_id,
|
||||
|
||||
Reference in New Issue
Block a user