{"record":{"id":"e4a4ba8edb4c2160","repo":"Freika/dawarich","slug":"invalid-url-scheme-scheme","errorCode":null,"errorMessage":"Invalid URL scheme: %{scheme}","messagePattern":"Invalid URL scheme: %(.+?)","errorType":"validation","errorClass":"UrlValidatable::BlockedUrlError","httpStatus":null,"severity":"warning","filePath":"app/services/concerns/url_validatable.rb","lineNumber":64,"sourceCode":"    IPAddr.new('10.0.0.0/8'),      # RFC1918\n    IPAddr.new('100.64.0.0/10'),   # CGNAT (Tailscale uses this)\n    IPAddr.new('127.0.0.0/8'),     # IPv4 loopback\n    IPAddr.new('172.16.0.0/12'),   # RFC1918\n    IPAddr.new('192.0.0.0/24'),    # IETF protocol assignments\n    IPAddr.new('192.168.0.0/16'),  # RFC1918\n    IPAddr.new('198.18.0.0/15'),   # benchmark\n    IPAddr.new('::1/128'),         # IPv6 loopback\n    IPAddr.new('fc00::/7')         # IPv6 ULA\n  ].freeze\n\n  private\n\n  def validate_integration_url!(url)\n    return if url.blank?\n\n    uri = URI.parse(url)\n    unless %w[http https].include?(uri.scheme)\n      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.invalid_scheme', scheme: uri.scheme)\n    end\n    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.host_required') if uri.host.blank?\n\n    # Cloud refuses URLs that embed credentials. Self-hosters legitimately\n    # use http://user:pass@host — homelab Immich behind nginx basic-auth\n    # is a real config we don't want to break.\n    if uri.userinfo.present? && !DawarichSettings.self_hosted?\n      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.embedded_credentials')\n    end\n\n    ip = IPAddr.new(Resolv.getaddress(uri.host))\n    if blocked_ranges.any? { |range| range.include?(ip) }\n      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.blocked_address')\n    end\n  rescue URI::InvalidURIError\n    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.invalid_format')\n  rescue Resolv::ResolvError\n    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.unresolvable_host', host: uri.host)","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/services/concerns/url_validatable.rb#L46-L82","documentation":"Raised as BlockedUrlError by Imports/UrlValidatable#validate_integration_url! when the parsed URL's scheme is anything other than http or https. Typical offenders: 'ftp://', 'file://', 'webdav://', a bare 'host/path' that URI.parse gives a nil scheme for, or typos like 'http//host'. It fires before DNS resolution, so it is purely a lexical scheme check.","triggerScenarios":"User saves an integration URL (Immich, PhotoPrism, OwnTracks endpoint) typed as 'immich.example.com' without a scheme (scheme is nil), 'ftp://server/export', or with a malformed prefix like 'http//:'. Also API clients posting url: 'localhost:2283' without http://.","commonSituations":"Users pasting hostnames copied from docs that omit the protocol, mobile clients constructing URLs from a host field, typos when editing self-hosted integration settings, URLs with trailing spaces or unicode characters that break URI.parse's scheme detection.","solutions":["Fix the stored URL to start with http:// or https:// (e.g. 'https://immich.example.com').","If the input is a bare host, have the UI prepend a default scheme or normalize client-side before submitting.","If the scheme looks present but still fails, print URI.parse(url).scheme to see what Ruby actually extracted (nil, 'ftp', garbage).","Add a client-side pattern check like \\Ahttps?:// before allowing save."],"exampleFix":"# before\nurl = 'immich.lan:2283'           # URI scheme is nil\nvalidate_integration_url!(url)    # -> BlockedUrlError: Invalid URL scheme\n\n# after\nurl = 'http://immich.lan:2283'\nvalidate_integration_url!(url)     # scheme 'http' passes","handlingStrategy":"validation","validationCode":"require 'uri'\nuri = URI.parse(url.to_s)\nuri.scheme.in?(%w[http https]) # gate before save","typeGuard":"def http_url?(s)\n  u = URI.parse(s.to_s)\n  %w[http https].include?(u.scheme) && u.host.present?\nrescue URI::InvalidURIError\n  false\nend","tryCatchPattern":"begin\n  validate_integration_url!(url)\nrescue BlockedUrlError => e\n  errors.add(:url, e.message)\nend","preventionTips":["Validate scheme client-side with a \\Ahttps?:// pattern before allowing the form to save.","Auto-prepend https:// when the user typed a bare host.","Store the original raw value in the error report so typos are obvious."],"tags":["url-validation","input-validation","integrations","security","ruby"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}