Skip to content

Commit c9db9dc

Browse files
committed
Redirect gateway-like urls to ipfs://
Resolves brave/brave-browser#21454 Urls in format of https://bafy.ipfs.gateway.io or https://gateway.io/ipfs/bafy are now redirected to ipfs:// scheme if x-ipfs-path header is received
1 parent 23c1718 commit c9db9dc

7 files changed

+394
-38
lines changed

browser/ipfs/test/ipfs_service_browsertest.cc

+266-18
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,20 @@ class IpfsServiceBrowserTest : public InProcessBrowserTest {
395395
return http_response;
396396
}
397397

398+
std::unique_ptr<net::test_server::HttpResponse> HandlePublicGatewayRequest(
399+
const net::test_server::HttpRequest& request) {
400+
auto http_response =
401+
std::make_unique<net::test_server::BasicHttpResponse>();
402+
http_response->set_content_type("text/html");
403+
404+
// IPFS gateways set this
405+
http_response->AddCustomHeader("access-control-allow-origin", "*");
406+
http_response->AddCustomHeader("x-ipfs-path", "/ipfs/Qmm");
407+
http_response->set_code(net::HTTP_OK);
408+
409+
return http_response;
410+
}
411+
398412
std::unique_ptr<net::test_server::HttpResponse> HandleEmbeddedSrvrRequest(
399413
const net::test_server::HttpRequest& request) {
400414
auto http_response =
@@ -1053,57 +1067,291 @@ IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, CannotLoadIPFSImageFromHTTP) {
10531067
EXPECT_EQ(base::Value(true), loaded.value);
10541068
}
10551069

1056-
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, TopLevelAutoRedirectsOn) {
1070+
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
1071+
TopLevelAutoRedirectsOff_DoNotTranslateToIPFS) {
1072+
ResetTestServer(
1073+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1074+
base::Unretained(this)));
1075+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, false);
1076+
browser()->profile()->GetPrefs()->SetInteger(
1077+
kIPFSResolveMethod,
1078+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
1079+
GURL gateway = GetURL("b.com", "/");
1080+
SetIPFSDefaultGatewayForTest(gateway);
1081+
1082+
auto tab_url = GetURL(
1083+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
1084+
"/simple.html?a=b");
1085+
1086+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1087+
content::WebContents* contents =
1088+
browser()->tab_strip_model()->GetActiveWebContents();
1089+
EXPECT_EQ(contents->GetURL(), tab_url);
1090+
}
1091+
1092+
IN_PROC_BROWSER_TEST_F(
1093+
IpfsServiceBrowserTest,
1094+
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IpfsDisabled) {
1095+
ResetTestServer(
1096+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1097+
base::Unretained(this)));
1098+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1099+
browser()->profile()->GetPrefs()->SetInteger(
1100+
kIPFSResolveMethod,
1101+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_DISABLED));
1102+
GURL gateway = GetURL("b.com", "/");
1103+
SetIPFSDefaultGatewayForTest(gateway);
1104+
1105+
auto tab_url = GetURL(
1106+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
1107+
"/simple.html?a=b");
1108+
1109+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1110+
content::WebContents* contents =
1111+
browser()->tab_strip_model()->GetActiveWebContents();
1112+
EXPECT_EQ(contents->GetURL(), tab_url);
1113+
}
1114+
1115+
IN_PROC_BROWSER_TEST_F(
1116+
IpfsServiceBrowserTest,
1117+
TopLevelAutoRedirectsOn_Gateway_RedirectFromGatewayLikeUrl_IpfsSubDomain) {
1118+
ResetTestServer(
1119+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1120+
base::Unretained(this)));
1121+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1122+
browser()->profile()->GetPrefs()->SetInteger(
1123+
kIPFSResolveMethod,
1124+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
1125+
GURL gateway = GetURL("b.com", "/");
1126+
SetIPFSDefaultGatewayForTest(gateway);
1127+
1128+
auto tab_url = GetURL(
1129+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
1130+
"/simple.html?a=b");
1131+
1132+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1133+
content::WebContents* contents =
1134+
browser()->tab_strip_model()->GetActiveWebContents();
1135+
EXPECT_EQ(
1136+
contents->GetURL(),
1137+
GetURL(
1138+
"b.com",
1139+
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1140+
"simple.html?a=b"));
1141+
}
1142+
1143+
IN_PROC_BROWSER_TEST_F(
1144+
IpfsServiceBrowserTest,
1145+
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IfGatewayUrl) {
1146+
ResetTestServer(
1147+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1148+
base::Unretained(this)));
1149+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1150+
browser()->profile()->GetPrefs()->SetInteger(
1151+
kIPFSResolveMethod,
1152+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
1153+
GURL gateway = GetURL("b.com", "/");
1154+
SetIPFSDefaultGatewayForTest(gateway);
1155+
1156+
auto tab_url = GetURL(
1157+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.b.com",
1158+
"/simple.html?a=b");
1159+
1160+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1161+
content::WebContents* contents =
1162+
browser()->tab_strip_model()->GetActiveWebContents();
1163+
EXPECT_EQ(contents->GetURL(), tab_url);
1164+
}
1165+
1166+
IN_PROC_BROWSER_TEST_F(
1167+
IpfsServiceBrowserTest,
1168+
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IfLocalhostUrl) {
1169+
ResetTestServer(
1170+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1171+
base::Unretained(this)));
1172+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1173+
browser()->profile()->GetPrefs()->SetInteger(
1174+
kIPFSResolveMethod,
1175+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
1176+
GURL gateway = GetURL("b.com", "/");
1177+
SetIPFSDefaultGatewayForTest(gateway);
1178+
1179+
auto tab_url = GetURL(
1180+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs."
1181+
"localhost",
1182+
"/simple.html?a=b");
1183+
1184+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1185+
content::WebContents* contents =
1186+
browser()->tab_strip_model()->GetActiveWebContents();
1187+
EXPECT_EQ(contents->GetURL(), tab_url);
1188+
}
1189+
1190+
IN_PROC_BROWSER_TEST_F(
1191+
IpfsServiceBrowserTest,
1192+
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_NoXIpfsPathHeader) {
10571193
ResetTestServer(
10581194
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
10591195
base::Unretained(this)));
10601196
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1197+
browser()->profile()->GetPrefs()->SetInteger(
1198+
kIPFSResolveMethod,
1199+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
10611200
GURL gateway = GetURL("b.com", "/");
10621201
SetIPFSDefaultGatewayForTest(gateway);
1063-
auto tab_url = GetURL("a.com", "/simple.html");
1202+
1203+
auto tab_url =
1204+
GetURL("a.com",
1205+
"/ipfs/"
1206+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq");
1207+
10641208
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
10651209
content::WebContents* contents =
10661210
browser()->tab_strip_model()->GetActiveWebContents();
1067-
EXPECT_EQ(contents->GetURL().host(), tab_url.host());
1211+
EXPECT_EQ(contents->GetURL(), tab_url);
1212+
}
10681213

1214+
IN_PROC_BROWSER_TEST_F(
1215+
IpfsServiceBrowserTest,
1216+
TopLevelAutoRedirectsOn_Gateway_RedirectFromGatewayLikeUrl_IpfsPath) {
1217+
ResetTestServer(
1218+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1219+
base::Unretained(this)));
1220+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
10691221
browser()->profile()->GetPrefs()->SetInteger(
10701222
kIPFSResolveMethod,
10711223
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
1072-
tab_url = GURL("ipfs://Qmc2JTQo4iXf24g98otZmGFQq176eQ2Cdbb88qA5ToMEvC/2");
1224+
GURL gateway = GetURL("b.com", "/");
1225+
SetIPFSDefaultGatewayForTest(gateway);
1226+
1227+
auto tab_url = GetURL(
1228+
"a.com",
1229+
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1230+
"simple.html?a=b");
1231+
1232+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1233+
content::WebContents* contents =
1234+
browser()->tab_strip_model()->GetActiveWebContents();
1235+
EXPECT_EQ(
1236+
contents->GetURL(),
1237+
GetURL(
1238+
"b.com",
1239+
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1240+
"simple.html?a=b"));
1241+
}
1242+
1243+
IN_PROC_BROWSER_TEST_F(
1244+
IpfsServiceBrowserTest,
1245+
TopLevelAutoRedirectsOn_ASK_RedirectFromGatewayLikeUrl_IpfsPath) {
1246+
ResetTestServer(
1247+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1248+
base::Unretained(this)));
1249+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1250+
browser()->profile()->GetPrefs()->SetInteger(
1251+
kIPFSResolveMethod,
1252+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
1253+
GURL gateway = GetURL("b.com", "/");
1254+
SetIPFSDefaultGatewayForTest(gateway);
1255+
1256+
auto tab_url = GetURL(
1257+
"a.com",
1258+
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1259+
"simple.html?a=b");
1260+
1261+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1262+
content::WebContents* contents =
1263+
browser()->tab_strip_model()->GetActiveWebContents();
1264+
EXPECT_EQ(
1265+
contents->GetURL(),
1266+
GURL("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1267+
"simple.html?a=b"));
1268+
}
1269+
1270+
IN_PROC_BROWSER_TEST_F(
1271+
IpfsServiceBrowserTest,
1272+
TopLevelAutoRedirectsOn_ASK_RedirectFromGatewayLikeUrl_IpfsSubDomain) {
1273+
ResetTestServer(
1274+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1275+
base::Unretained(this)));
1276+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1277+
browser()->profile()->GetPrefs()->SetInteger(
1278+
kIPFSResolveMethod,
1279+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
1280+
GURL gateway = GetURL("b.com", "/");
1281+
SetIPFSDefaultGatewayForTest(gateway);
1282+
1283+
auto tab_url = GetURL(
1284+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
1285+
"/simple.html?a=b");
1286+
10731287
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1074-
auto domain = GetDomainAndRegistry(
1288+
content::WebContents* contents =
1289+
browser()->tab_strip_model()->GetActiveWebContents();
1290+
EXPECT_EQ(
10751291
contents->GetURL(),
1076-
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
1292+
GURL("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
1293+
"simple.html?a=b"));
1294+
}
1295+
1296+
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
1297+
TopLevelAutoRedirectsOn_DoNotTranslateSimpleUrls) {
1298+
ResetTestServer(
1299+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
1300+
base::Unretained(this)));
1301+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1302+
browser()->profile()->GetPrefs()->SetInteger(
1303+
kIPFSResolveMethod,
1304+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
1305+
GURL gateway = GetURL("b.com", "/");
1306+
SetIPFSDefaultGatewayForTest(gateway);
10771307

1078-
EXPECT_EQ(domain, gateway.host());
1308+
auto tab_url = GetURL("a.com", "/simple.html?a=b");
1309+
1310+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
1311+
content::WebContents* contents =
1312+
browser()->tab_strip_model()->GetActiveWebContents();
1313+
EXPECT_EQ(contents->GetURL(), tab_url);
10791314
}
10801315

10811316
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
1082-
TopLevelAutoRedirectsOnWithQuery) {
1317+
TopLevelAutoRedirectsOn_DoNotTranslateIncompleteUrls) {
10831318
ResetTestServer(
1084-
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
1319+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
10851320
base::Unretained(this)));
10861321
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
1322+
browser()->profile()->GetPrefs()->SetInteger(
1323+
kIPFSResolveMethod,
1324+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
10871325
GURL gateway = GetURL("b.com", "/");
10881326
SetIPFSDefaultGatewayForTest(gateway);
1089-
ASSERT_TRUE(ui_test_utils::NavigateToURL(
1090-
browser(), GetURL("a.com", "/simple.html?abc=123xyz&other=qwerty")));
1327+
1328+
auto tab_url = GetURL("ipfs.a.com", "/simple.html?a=b");
1329+
1330+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
10911331
content::WebContents* contents =
10921332
browser()->tab_strip_model()->GetActiveWebContents();
1093-
EXPECT_EQ(contents->GetURL().query(), "abc=123xyz&other=qwerty");
1333+
EXPECT_EQ(contents->GetURL(), tab_url);
10941334
}
10951335

10961336
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, TopLevelAutoRedirectsOff) {
10971337
ResetTestServer(
1098-
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
1338+
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
10991339
base::Unretained(this)));
1100-
SetIPFSDefaultGatewayForTest(GetURL("b.com", "/"));
1101-
GURL other_gateway = GetURL("a.com", "/simple.html");
1102-
ASSERT_TRUE(
1103-
ui_test_utils::NavigateToURL(browser(), GetURL("a.com", "/simple.html")));
1340+
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, false);
1341+
browser()->profile()->GetPrefs()->SetInteger(
1342+
kIPFSResolveMethod,
1343+
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
1344+
GURL gateway = GetURL("b.com", "/");
1345+
SetIPFSDefaultGatewayForTest(gateway);
1346+
1347+
auto tab_url = GetURL(
1348+
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
1349+
"/simple.html?a=b");
1350+
1351+
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
11041352
content::WebContents* contents =
11051353
browser()->tab_strip_model()->GetActiveWebContents();
1106-
EXPECT_EQ(contents->GetURL().host(), other_gateway.host());
1354+
EXPECT_EQ(contents->GetURL(), tab_url);
11071355
}
11081356

11091357
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, ImportTextToIpfs) {

browser/net/ipfs_redirect_network_delegate_helper.cc

+15-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "components/user_prefs/user_prefs.h"
1414
#include "content/public/browser/browser_context.h"
1515
#include "net/base/net_errors.h"
16+
#include "net/base/url_util.h"
1617

1718
namespace ipfs {
1819

@@ -94,6 +95,13 @@ int OnHeadersReceived_IPFSRedirectWork(
9495
std::shared_ptr<brave::BraveRequestInfo> ctx) {
9596
if (!ctx->browser_context)
9697
return net::OK;
98+
99+
// Auto-redirect gateway-like urls is enabled only for top-level frames
100+
// to avoid mixed content corner cases.
101+
if (ctx->resource_type == blink::mojom::ResourceType::kSubFrame) {
102+
return net::OK;
103+
}
104+
97105
auto* prefs = user_prefs::UserPrefs::Get(ctx->browser_context);
98106
if (IsIpfsResolveMethodDisabled(prefs)) {
99107
return net::OK;
@@ -105,24 +113,21 @@ int OnHeadersReceived_IPFSRedirectWork(
105113
response_headers->GetNormalizedHeader("x-ipfs-path", &ipfs_path) &&
106114
// Make sure we don't infinite redirect
107115
!ctx->request_url.DomainIs(ctx->ipfs_gateway_url.host()) &&
108-
// Do not redirect if the frame is not ipfs/ipns
109-
IsIPFSScheme(ctx->initiator_url)) {
110-
GURL::Replacements replacements;
111-
replacements.SetPathStr(ipfs_path);
116+
!net::IsLocalhost(ctx->request_url)) {
117+
auto translated_url = ipfs::TranslateToCurrentGatewayUrl(ctx->request_url);
112118

113-
if (ctx->request_url.has_query()) {
114-
replacements.SetQueryStr(ctx->request_url.query_piece());
119+
if (!translated_url) {
120+
return net::OK;
115121
}
116122

117-
GURL new_url = ctx->ipfs_gateway_url.ReplaceComponents(replacements);
118-
119123
*override_response_headers =
120124
new net::HttpResponseHeaders(response_headers->raw_headers());
121125
(*override_response_headers)
122126
->ReplaceStatusLine("HTTP/1.1 307 Temporary Redirect");
123127
(*override_response_headers)->RemoveHeader("Location");
124-
(*override_response_headers)->AddHeader("Location", new_url.spec());
125-
*allowed_unsafe_redirect_url = new_url;
128+
(*override_response_headers)
129+
->AddHeader("Location", translated_url.value().spec());
130+
*allowed_unsafe_redirect_url = translated_url.value();
126131
}
127132

128133
return net::OK;

0 commit comments

Comments
 (0)