fix: address Shannon security assessment findings (37 vulnerabilities) (#254)
Remediate 4 Critical, 18 High, 10 Medium, and 5 Low vulnerabilities identified in the Shannon security assessment (2026-03-20). Critical fixes: - Command injection via rsync SSH key path (INJ-VULN-01) - Self-escalation to super_admin role (AUTHZ-VULN-11) - Invite super_admin backdoor (AUTHZ-VULN-12) - Handlebars SSTI in email templates (INJ-VULN-05) Authentication hardening: - Rate limit on share-link login (AUTH-VULN-01) - X-Forwarded-For spoofing bypass (AUTH-VULN-02) - reCAPTCHA fails closed when misconfigured (AUTH-VULN-03) - Token revocation on admin/gallery logout (AUTH-VULN-04/05) - Cookie Secure flag defaults true in production (AUTH-VULN-06) - Remove JWT from admin login response body (AUTH-VULN-07) - Timing-safe gallery slug validation (AUTH-VULN-09) - Account lockout fails closed on DB error (AUTH-VULN-12) - Session endpoint checks token revocation Path traversal & file access: - checksums endpoint path containment (INJ-VULN-03) - manifest validate path containment (INJ-VULN-04) XSS prevention: - Block SVG data URIs in CSS sanitizer (XSS-VULN-01) - Email preview iframe sandbox (XSS-VULN-02) - SSR branding HTML escaping (XSS-VULN-03) - User-Agent sanitization in feedback (XSS-VULN-04) Authorization (IDOR): - Event ownership middleware for all admin routes - Cross-admin user profile read restriction (AUTHZ-VULN-10) SSRF & infrastructure: - Private IP validation for SMTP, S3, rsync hosts - Replace inline JWT with standard adminAuth middleware - CSRF Content-Type enforcement on mutating API endpoints - CSP headers in nginx location blocks Token revocation fix: - Remove overly broad orWhere clause that invalidated all future tokens - Allow empty-body POST requests (logout) in CSRF middleware Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
co-authored by
Paul Nothaft
parent
a63f1a8dd9
commit
23cd9cb680
@@ -345,15 +345,16 @@ async function processTemplate(template, variables, language = 'en') {
|
||||
processedVariables.welcome_message = formatWelcomeMessage(processedVariables.welcome_message);
|
||||
}
|
||||
|
||||
// Compile templates with Handlebars
|
||||
const subjectTemplate = Handlebars.compile(subject);
|
||||
const htmlTemplate = Handlebars.compile(htmlBody);
|
||||
const textTemplate = Handlebars.compile(textBody);
|
||||
// Safe template replacement (no code execution, only simple variable substitution)
|
||||
function safeTemplateReplace(template, variables) {
|
||||
return template.replace(/\{\{(\w+)\}\}/g, (match, key) =>
|
||||
variables.hasOwnProperty(key) ? String(variables[key]) : match
|
||||
);
|
||||
}
|
||||
|
||||
// Process templates with processedVariables (includes formatted dates and security messages)
|
||||
subject = subjectTemplate(processedVariables);
|
||||
htmlBody = htmlTemplate(processedVariables);
|
||||
textBody = textTemplate(processedVariables);
|
||||
subject = safeTemplateReplace(subject, processedVariables);
|
||||
htmlBody = safeTemplateReplace(htmlBody, processedVariables);
|
||||
textBody = safeTemplateReplace(textBody, processedVariables);
|
||||
|
||||
// Inject client access section if client_link is provided (#172)
|
||||
if (processedVariables.client_link) {
|
||||
|
||||
Reference in New Issue
Block a user