{"record":{"id":"8fedb844d3b8bd1a","repo":"maotoumao/MusicFree","slug":"dialog-markdowndialog-openexternallink","errorCode":null,"errorMessage":"dialog.markdownDialog.openExternalLink","messagePattern":"dialog\\.markdownDialog\\.openExternalLink","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src/components/dialogs/components/markdownDialog.tsx","lineNumber":279,"sourceCode":"        {\n            title: okText ?? t(\"dialog.errorLogKnow\"),\n            type: \"primary\",\n            onPress() {\n                hideDialog();\n            },\n        },\n    ] as any;\n\n    return (\n        <Dialog onDismiss={hideDialog} >\n            <Dialog.Title withDivider>{title}</Dialog.Title>\n            <Dialog.Content style={[{ height: vh(60), maxHeight: vh(60) }, styles.dialogContent]}>\n                {loading ? <Loading /> : <WebView style={styles.webView} originWhitelist={[\"*\"]} source={{\n                    html: htmlContent,\n                }}\n                onShouldStartLoadWithRequest={(event) => {\n                    if (event.url.startsWith(\"http\") || event.url.startsWith(\"https\")) {\n                        Toast.warn(i18n.t(\"dialog.markdownDialog.openExternalLink\"), {\n                            type: \"warn\",\n                            duration: 3000,\n                            actionText: i18n.t(\"common.open\"),\n                            onActionClick() {\n                                openUrl(event.url);\n                            },\n                        });\n                    }\n                    return false;\n                }}\n\n                />}\n            </Dialog.Content>\n            <Dialog.Actions actions={actions} />\n        </Dialog>\n    );\n}\n","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/maotoumao/MusicFree/blob/d118b18b3d0c904400f7eea7bf99c0ceec6c1aee/src/components/dialogs/components/markdownDialog.tsx#L261-L297","documentation":"MarkdownDialog renders untrusted markdown as HTML in a WebView. Its `onShouldStartLoadWithRequest` blocks any http(s) navigation inside the WebView and instead shows a warning toast keyed by `dialog.markdownDialog.openExternalLink`, offering an \"打开\" (open) action that forwards the URL to the system via openUrl(). This is intentional policy: external links must never load inside the sandboxed WebView.","triggerScenarios":"A user taps any http/https link inside markdown-rendered content (song descriptions, plugin docs, changelogs); the WebView's shouldStartLoadWithRequest callback intercepts it and raises the localized warning toast with the open action.","commonSituations":"Reading a plugin's README inside the markdown dialog and clicking a GitHub link; clicking an image whose src is remote http(s); authors embedding external links in descriptions; users confused why links don't navigate in place.","solutions":["This is expected behavior — tap the \"打开\" action on the toast to open the link in the system browser.","If the toast text appears untranslated, add the `dialog.markdownDialog.openExternalLink` key to the active locale's i18n resource file.","If no browser opens after tapping \"打开\", fix the underlying openUrl problem (scheme support / LSApplicationQueriesSchemes).","Plugin authors should keep links in markdown `[text](https://...)` form so they are intercepted and handled consistently."],"exampleFix":"// before: missing translation key causes raw key to display\n// locales/en.json without the key\n\n// after: add the key so users see a readable message\n// locales/en.json\n\"dialog\": { \"markdownDialog\": { \"openExternalLink\": \"External link blocked, open in browser?\" } }","handlingStrategy":"fallback","validationCode":"// inside MarkdownDialog, before rendering html\nconst safeHtml = htmlContent.replace(\n    /(https?:\\/\\/[^\\s\"'<>]+)/g,\n    '<a href=\"$1\" onclick=\"window.ReactNativeWebView && window.ReactNativeWebView.postMessage(JSON.stringify({type:\\'link\\',url:\\'$1\\'}));return false;\">$1</a>'\n);","typeGuard":"function isExternalHttpUrl(url: string): boolean {\n    return url.startsWith('http://') || url.startsWith('https://');\n}","tryCatchPattern":"onShouldStartLoadWithRequest={(event) => {\n    if (isExternalHttpUrl(event.url)) {\n        Toast.warn(i18n.t('dialog.markdownDialog.openExternalLink'), {\n            type: 'warn',\n            duration: 3000,\n            actionText: i18n.t('common.open'),\n            onActionClick() {\n                openUrl(event.url).catch(() =>\n                    Toast.warn(i18n.t('dialog.markdownDialog.openExternalLink')));\n            },\n        });\n        return false;\n    }\n    return true;\n}}","preventionTips":["Never allow http(s) navigation inside the sandboxed WebView — always intercept and open externally.","Ensure all locale files define dialog.markdownDialog.openExternalLink so users see readable text.","Keep the open action resilient: openUrl already swallows failures, but log the URL for diagnosis.","Escape/untrusted-render markdown before converting to HTML to avoid script injection via href attributes."],"tags":["webview","i18n","security","markdown"],"backgroundTag":"external-link-blocked","analyzedSha":"d118b18b3d0c904400f7eea7bf99c0ceec6c1aee","analyzedAt":"2026-08-30T11:49:12.932Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}