• Claude's avatar
    fix(website): guard editor-supplied URLs against unsafe schemes · c3f2ae53
    Claude authored
    Menu items, button links, announcement bars, popups, floating buttons and
    navbar CTAs all wrote editor-supplied values straight into href attributes.
    Any user with settings.manage could store a `javascript:` URL and have it run
    for every visitor of that tenant's public site.
    
    Adds safe_url(), which allow-lists http/https/mailto/tel/whatsapp, site-relative
    paths and fragments, and rejects protocol-relative URLs, data:, vbscript: and
    entity/whitespace/control-character obfuscation before testing the scheme.
    
    Applied at four layers so no writer can bypass it:
      - input     MenuManager rules + BlockField Link validation
      - model     WebsiteMenuItem::href()
      - render    every editor-supplied href in every website view
      - import    WebsiteBlueprintService, since blueprint files skip form rules
    
    safe_url() returns null rather than '#', so blocks skip the link entirely
    instead of emitting a dead anchor — this satisfies the project's no-href="#"
    rule with the same mechanism.
    
    Also fixes Alpine expression injection in the gallery and schedule lightboxes,
    where a quote inside an image URL could break out of the inline handler: Blade
    escapes ' to ' but the browser decodes it before Alpine parses.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    c3f2ae53
contact.blade.php 14.3 KB