mirror of
https://github.com/miniflux/v2.git
synced 2025-06-27 16:36:00 +00:00
refactor(sanitizer): use a map for iframe allow list
This commit is contained in:
parent
44c48d109f
commit
ac44507af2
5 changed files with 119 additions and 40 deletions
|
@ -2169,12 +2169,33 @@ func TestYouTubeApiKey(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDefaultYouTubeEmbedUrl(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
opts, err := NewParser().ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := "https://www.youtube-nocookie.com/embed/"
|
||||
result := opts.YouTubeEmbedUrlOverride()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected default value, got %v instead of %v`, result, expected)
|
||||
}
|
||||
|
||||
expected = "www.youtube-nocookie.com"
|
||||
result = opts.YouTubeEmbedDomain()
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected YouTube embed domain, got %v instead of %v`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestYouTubeEmbedUrlOverride(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("YOUTUBE_EMBED_URL_OVERRIDE", "https://invidious.custom/embed/")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
opts, err := NewParser().ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
@ -2185,6 +2206,12 @@ func TestYouTubeEmbedUrlOverride(t *testing.T) {
|
|||
if result != expected {
|
||||
t.Fatalf(`Unexpected YOUTUBE_EMBED_URL_OVERRIDE value, got %v instead of %v`, result, expected)
|
||||
}
|
||||
|
||||
expected = "invidious.custom"
|
||||
result = opts.YouTubeEmbedDomain()
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected YouTube embed domain, got %v instead of %v`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConfigDumpOutput(t *testing.T) {
|
||||
|
|
|
@ -155,6 +155,7 @@ type Options struct {
|
|||
filterEntryMaxAgeDays int
|
||||
youTubeApiKey string
|
||||
youTubeEmbedUrlOverride string
|
||||
youTubeEmbedDomain string
|
||||
oauth2UserCreationAllowed bool
|
||||
oauth2ClientID string
|
||||
oauth2ClientSecret string
|
||||
|
@ -521,11 +522,19 @@ func (o *Options) YouTubeApiKey() string {
|
|||
return o.youTubeApiKey
|
||||
}
|
||||
|
||||
// YouTubeEmbedUrlOverride returns YouTube URL which will be used for embeds
|
||||
// YouTubeEmbedUrlOverride returns the YouTube embed URL override if defined.
|
||||
func (o *Options) YouTubeEmbedUrlOverride() string {
|
||||
return o.youTubeEmbedUrlOverride
|
||||
}
|
||||
|
||||
// YouTubeEmbedDomain returns the domain used for YouTube embeds.
|
||||
func (o *Options) YouTubeEmbedDomain() string {
|
||||
if o.youTubeEmbedDomain != "" {
|
||||
return o.youTubeEmbedDomain
|
||||
}
|
||||
return "www.youtube-nocookie.com"
|
||||
}
|
||||
|
||||
// FetchNebulaWatchTime returns true if the Nebula video duration
|
||||
// should be fetched and used as a reading time.
|
||||
func (o *Options) FetchNebulaWatchTime() bool {
|
||||
|
|
|
@ -298,6 +298,13 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
|||
if port != "" {
|
||||
p.opts.listenAddr = ":" + port
|
||||
}
|
||||
|
||||
youtubeEmbedURL, err := url.Parse(p.opts.youTubeEmbedUrlOverride)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config: invalid YOUTUBE_EMBED_URL_OVERRIDE value: %w", err)
|
||||
}
|
||||
p.opts.youTubeEmbedDomain = youtubeEmbedURL.Hostname()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,21 @@ var (
|
|||
"munderover": {},
|
||||
"semantics": {},
|
||||
}
|
||||
|
||||
iframeAllowList = map[string]struct{}{
|
||||
"bandcamp.com": {},
|
||||
"cdn.embedly.com": {},
|
||||
"dailymotion.com": {},
|
||||
"open.spotify.com": {},
|
||||
"player.bilibili.com": {},
|
||||
"player.twitch.tv": {},
|
||||
"player.vimeo.com": {},
|
||||
"soundcloud.com": {},
|
||||
"vk.com": {},
|
||||
"w.soundcloud.com": {},
|
||||
"youtube-nocookie.com": {},
|
||||
"youtube.com": {},
|
||||
}
|
||||
)
|
||||
|
||||
type SanitizerOptions struct {
|
||||
|
@ -266,7 +281,7 @@ func sanitizeAttributes(parsedBaseUrl *url.URL, baseURL, tagName string, attribu
|
|||
if isExternalResourceAttribute(attribute.Key) {
|
||||
switch {
|
||||
case tagName == "iframe":
|
||||
if !isValidIframeSource(parsedBaseUrl, baseURL, attribute.Val) {
|
||||
if !isValidIframeSource(attribute.Val) {
|
||||
continue
|
||||
}
|
||||
value = rewriteIframeURL(attribute.Val)
|
||||
|
@ -448,39 +463,22 @@ func isBlockedResource(src string) bool {
|
|||
})
|
||||
}
|
||||
|
||||
func isValidIframeSource(parsedBaseUrl *url.URL, baseURL, src string) bool {
|
||||
whitelist := []string{
|
||||
"bandcamp.com",
|
||||
"cdn.embedly.com",
|
||||
"player.bilibili.com",
|
||||
"player.twitch.tv",
|
||||
"player.vimeo.com",
|
||||
"soundcloud.com",
|
||||
"vk.com",
|
||||
"w.soundcloud.com",
|
||||
"dailymotion.com",
|
||||
"youtube-nocookie.com",
|
||||
"youtube.com",
|
||||
"open.spotify.com",
|
||||
}
|
||||
domain := urllib.Domain(src)
|
||||
func isValidIframeSource(iframeSourceURL string) bool {
|
||||
iframeSourceDomain := strings.TrimPrefix(urllib.Domain(iframeSourceURL), "www.")
|
||||
|
||||
baseDomain := baseURL
|
||||
if parsedBaseUrl != nil {
|
||||
baseDomain = parsedBaseUrl.Hostname()
|
||||
}
|
||||
|
||||
// allow iframe from same origin
|
||||
if baseDomain == domain {
|
||||
if _, ok := iframeAllowList[iframeSourceDomain]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// allow iframe from custom invidious instance
|
||||
if config.Opts.InvidiousInstance() == domain {
|
||||
if ytDomain := config.Opts.YouTubeEmbedDomain(); ytDomain != "" && iframeSourceDomain == strings.TrimPrefix(ytDomain, "www.") {
|
||||
return true
|
||||
}
|
||||
|
||||
return slices.Contains(whitelist, strings.TrimPrefix(domain, "www."))
|
||||
if invidiousInstance := config.Opts.InvidiousInstance(); invidiousInstance != "" && iframeSourceDomain == strings.TrimPrefix(invidiousInstance, "www.") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func rewriteIframeURL(link string) string {
|
||||
|
|
|
@ -13,12 +13,6 @@ import (
|
|||
"miniflux.app/v2/internal/config"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
config.Opts = config.NewOptions()
|
||||
exitCode := m.Run()
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func BenchmarkSanitize(b *testing.B) {
|
||||
var testCases = map[string][]string{
|
||||
"miniflux_github.html": {"https://github.com/miniflux/v2", ""},
|
||||
|
@ -352,6 +346,8 @@ func TestInvalidNestedTag(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInvalidIFrame(t *testing.T) {
|
||||
config.Opts = config.NewOptions()
|
||||
|
||||
input := `<iframe src="http://example.org/"></iframe>`
|
||||
expected := ``
|
||||
output := SanitizeHTMLWithDefaultOptions("http://example.com/", input)
|
||||
|
@ -361,7 +357,51 @@ func TestInvalidIFrame(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSameDomainIFrame(t *testing.T) {
|
||||
config.Opts = config.NewOptions()
|
||||
|
||||
input := `<iframe src="http://example.com/test"></iframe>`
|
||||
expected := ``
|
||||
output := SanitizeHTMLWithDefaultOptions("http://example.com/", input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Wrong output: %q != %q`, expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvidiousIFrame(t *testing.T) {
|
||||
config.Opts = config.NewOptions()
|
||||
|
||||
input := `<iframe src="https://yewtu.be/watch?v=video_id"></iframe>`
|
||||
expected := `<iframe src="https://yewtu.be/watch?v=video_id" sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox" loading="lazy"></iframe>`
|
||||
output := SanitizeHTMLWithDefaultOptions("http://example.com/", input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Wrong output: %q != %q`, expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomYoutubeEmbedURL(t *testing.T) {
|
||||
os.Setenv("YOUTUBE_EMBED_URL_OVERRIDE", "https://www.invidious.custom/embed/")
|
||||
|
||||
defer os.Clearenv()
|
||||
var err error
|
||||
if config.Opts, err = config.NewParser().ParseEnvironmentVariables(); err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
input := `<iframe src="https://www.invidious.custom/embed/1234"></iframe>`
|
||||
expected := `<iframe src="https://www.invidious.custom/embed/1234" sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox" loading="lazy"></iframe>`
|
||||
output := SanitizeHTMLWithDefaultOptions("http://example.com/", input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Wrong output: %q != %q`, expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIFrameWithChildElements(t *testing.T) {
|
||||
config.Opts = config.NewOptions()
|
||||
|
||||
input := `<iframe src="https://www.youtube.com/"><p>test</p></iframe>`
|
||||
expected := `<iframe src="https://www.youtube.com/" sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox" loading="lazy"></iframe>`
|
||||
output := SanitizeHTMLWithDefaultOptions("http://example.com/", input)
|
||||
|
@ -750,13 +790,11 @@ func TestReplaceProtocolRelativeYoutubeURL(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestReplaceYoutubeURLWithCustomURL(t *testing.T) {
|
||||
os.Clearenv()
|
||||
defer os.Clearenv()
|
||||
os.Setenv("YOUTUBE_EMBED_URL_OVERRIDE", "https://invidious.custom/embed/")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
config.Opts, err = parser.ParseEnvironmentVariables()
|
||||
|
||||
config.Opts, err = config.NewParser().ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue