feat(security): enhance password validation and API key handling

- Strengthen password requirements with checks for uppercase, lowercase, numbers, and special characters (at least 3 types required)
- Block access to sensitive configuration files like qbm_settings.yml
- Improve API key display in UI with password input and show/hide toggle for better security
This commit is contained in:
bobokun 2025-09-07 15:56:15 -04:00
parent 0dd5be6fdd
commit 8aa0751c74
No known key found for this signature in database
GPG key ID: B73932169607D927
4 changed files with 32 additions and 9 deletions

View file

@ -1 +1 @@
4.6.1-develop20
4.6.1-develop21

View file

@ -93,8 +93,25 @@ class SecuritySettingsRequest(BaseModel):
@validator("password")
def password_must_be_strong(cls, v):
if v and len(v) < 8:
raise ValueError("Password must be at least 8 characters")
if v:
if len(v) < 8:
raise ValueError("Password must be at least 8 characters")
# Check for character type requirements
has_upper = bool(re.search(r"[A-Z]", v))
has_lower = bool(re.search(r"[a-z]", v))
has_number = bool(re.search(r"\d", v))
has_special = bool(re.search(r"[!@#$%^&*]", v))
# Count how many character types are present
type_count = sum([has_upper, has_lower, has_number, has_special])
if type_count < 3:
raise ValueError(
"Password must contain at least 3 of: uppercase letters, "
"lowercase letters, numbers, special characters (!@#$%^&*)"
)
return v
@validator("method")

View file

@ -746,6 +746,10 @@ class WebAPI:
# Validate filename to prevent path traversal and block sensitive files
config_file_path = self._validate_config_filename(filename)
# Explicitly block access to sensitive settings file
if filename == "qbm_settings.yml":
raise HTTPException(status_code=403, detail="Access to settings file is forbidden")
if not config_file_path.exists():
raise HTTPException(status_code=404, detail=f"Configuration file '{filename}' not found")
@ -1669,11 +1673,6 @@ class WebAPI:
# Don't return sensitive information for security
settings.password_hash = "***" if settings.password_hash else ""
# Show only last 4 characters of API key for verification
if settings.api_key:
settings.api_key = f"***{settings.api_key[-4:]}" if len(settings.api_key) > 4 else "***"
else:
settings.api_key = ""
return settings
except Exception as e:

View file

@ -134,7 +134,14 @@ export class SecurityComponent {
<div class="form-group">
<label for="api-key-display" class="form-label">API Key</label>
<div class="api-key-input-group">
<input type="text" id="api-key-display" class="form-input" readonly value="${this.currentSettings.api_key || ''}" placeholder="No API key generated">
<div class="password-input-group">
<input type="password" id="api-key-display" class="form-input" readonly value="${this.currentSettings.api_key || ''}" placeholder="No API key generated">
${this.currentSettings.api_key ? `
<button type="button" class="btn btn-icon password-toggle" data-target="api-key-display" title="Show full API key">
${EYE_ICON_SVG}
</button>
` : ''}
</div>
<button type="button" class="btn btn-secondary" id="generate-api-key">
${this.currentSettings.api_key ? 'Generate New Key' : 'Generate Key'}
</button>