mirror of
https://git.sr.ht/~cadence/bibliogram
synced 2024-11-22 08:07:30 +00:00
Fix exploit and add tests for proxy URL validator
This commit is contained in:
parent
10c913a919
commit
6e1e5e6996
@ -1,3 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Check that a host is part of Instagram's CDN.
|
||||||
|
* @param {string} host
|
||||||
|
*/
|
||||||
|
function verifyHost(host) {
|
||||||
|
const domains = ["fbcdn.net", "cdninstagram.com"]
|
||||||
|
return domains.some(against => host === against || host.endsWith("." + against))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a resource is on Instagram.
|
||||||
|
* @param {URL} completeURL
|
||||||
|
*/
|
||||||
|
function verifyURL(completeURL) {
|
||||||
|
const params = completeURL.searchParams
|
||||||
|
if (!params.get("url")) return {status: "fail", value: [400, "Must supply `url` query parameter"]}
|
||||||
|
try {
|
||||||
|
var url = new URL(params.get("url"))
|
||||||
|
} catch (e) {
|
||||||
|
return {status: "fail", value: [400, "`url` query parameter is not a valid URL"]}
|
||||||
|
}
|
||||||
|
// check url protocol
|
||||||
|
if (url.protocol !== "https:") return {status: "fail", value: [400, "URL protocol must be `https:`"]}
|
||||||
|
// check url host
|
||||||
|
if (!verifyHost(url.host)) return {status: "fail", value: [400, "URL host is not allowed"]}
|
||||||
|
return {status: "ok", url}
|
||||||
|
}
|
||||||
|
|
||||||
function proxyImage(url, width) {
|
function proxyImage(url, width) {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
if (width) params.set("width", width)
|
if (width) params.set("width", width)
|
||||||
@ -23,7 +51,7 @@ function proxyVideo(url) {
|
|||||||
*/
|
*/
|
||||||
function proxyExtendedOwner(owner) {
|
function proxyExtendedOwner(owner) {
|
||||||
const clone = {...owner}
|
const clone = {...owner}
|
||||||
clone.profile_pic_url = proxyImage(clone.profile_pic_url)
|
clone.profile_pic_url = proxyProfilePic(clone.profile_pic_url, clone.id)
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,3 +59,5 @@ module.exports.proxyImage = proxyImage
|
|||||||
module.exports.proxyProfilePic = proxyProfilePic
|
module.exports.proxyProfilePic = proxyProfilePic
|
||||||
module.exports.proxyVideo = proxyVideo
|
module.exports.proxyVideo = proxyVideo
|
||||||
module.exports.proxyExtendedOwner = proxyExtendedOwner
|
module.exports.proxyExtendedOwner = proxyExtendedOwner
|
||||||
|
module.exports.verifyHost = verifyHost
|
||||||
|
module.exports.verifyURL = verifyURL
|
||||||
|
@ -3,27 +3,10 @@ const sharp = require("sharp")
|
|||||||
const constants = require("../../lib/constants")
|
const constants = require("../../lib/constants")
|
||||||
const collectors = require("../../lib/collectors")
|
const collectors = require("../../lib/collectors")
|
||||||
const {request} = require("../../lib/utils/request")
|
const {request} = require("../../lib/utils/request")
|
||||||
|
const {verifyURL} = require("../../lib/utils/proxyurl")
|
||||||
const db = require("../../lib/db")
|
const db = require("../../lib/db")
|
||||||
require("../../lib/testimports")(constants, request, db)
|
require("../../lib/testimports")(constants, request, db, verifyURL)
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that a resource is on Instagram.
|
|
||||||
* @param {URL} completeURL
|
|
||||||
*/
|
|
||||||
function verifyURL(completeURL) {
|
|
||||||
const params = completeURL.searchParams
|
|
||||||
if (!params.get("url")) return {status: "fail", value: [400, "Must supply `url` query parameter"]}
|
|
||||||
try {
|
|
||||||
var url = new URL(params.get("url"))
|
|
||||||
} catch (e) {
|
|
||||||
return {status: "fail", value: [400, "`url` query parameter is not a valid URL"]}
|
|
||||||
}
|
|
||||||
// check url protocol
|
|
||||||
if (url.protocol !== "https:") return {status: "fail", value: [400, "URL protocol must be `https:`"]}
|
|
||||||
// check url host
|
|
||||||
if (!["fbcdn.net", "cdninstagram.com"].some(host => url.host.endsWith(host))) return {status: "fail", value: [400, "URL host is not allowed"]}
|
|
||||||
return {status: "ok", url}
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusCodeIsAcceptable(status) {
|
function statusCodeIsAcceptable(status) {
|
||||||
return (status >= 200 && status < 300) || status === 304
|
return (status >= 200 && status < 300) || status === 304
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const tap = require("tap")
|
const tap = require("tap")
|
||||||
const {proxyImage, proxyVideo, proxyExtendedOwner} = require("../src/lib/utils/proxyurl")
|
const {proxyImage, proxyVideo, proxyProfilePic, proxyExtendedOwner, verifyHost, verifyURL} = require("../src/lib/utils/proxyurl")
|
||||||
|
|
||||||
tap.equal(
|
tap.equal(
|
||||||
proxyImage("https://scontent-syd2-1.cdninstagram.com/v/t51.2885-15/e35/p1080x1080/83429487_106792960779790_3699017977444758529_n.jpg?_nc_ht=scontent-syd2-1.cdninstagram.com&_nc_cat=1&_nc_ohc=YYmv6lkrblAAX_9u9Kt&oh=81a70f2b92e70873b5ebc9253e7df937&oe=5EBC230A"),
|
proxyImage("https://scontent-syd2-1.cdninstagram.com/v/t51.2885-15/e35/p1080x1080/83429487_106792960779790_3699017977444758529_n.jpg?_nc_ht=scontent-syd2-1.cdninstagram.com&_nc_cat=1&_nc_ohc=YYmv6lkrblAAX_9u9Kt&oh=81a70f2b92e70873b5ebc9253e7df937&oe=5EBC230A"),
|
||||||
@ -29,7 +29,7 @@ tap.test("proxy extended owner", childTest => {
|
|||||||
username: "instagram",
|
username: "instagram",
|
||||||
is_verified: true,
|
is_verified: true,
|
||||||
full_name: "Instagram",
|
full_name: "Instagram",
|
||||||
profile_pic_url: "/imageproxy?url=https%3A%2F%2Fscontent-syd2-1.cdninstagram.com%2Fv%2Ft51.2885-19%2Fs150x150%2F59381178_2348911458724961_5863612957363011584_n.jpg%3F_nc_ht%3Dscontent-syd2-1.cdninstagram.com%26_nc_ohc%3DTrMM-1zPSA4AX857GJB%26oh%3D15b843b0c1033784492b64b1170cd048%26oe%3D5EC9125D"
|
profile_pic_url: "/imageproxy?userID=25025320&url=https%3A%2F%2Fscontent-syd2-1.cdninstagram.com%2Fv%2Ft51.2885-19%2Fs150x150%2F59381178_2348911458724961_5863612957363011584_n.jpg%3F_nc_ht%3Dscontent-syd2-1.cdninstagram.com%26_nc_ohc%3DTrMM-1zPSA4AX857GJB%26oh%3D15b843b0c1033784492b64b1170cd048%26oe%3D5EC9125D"
|
||||||
},
|
},
|
||||||
"owner was proxied"
|
"owner was proxied"
|
||||||
)
|
)
|
||||||
@ -40,3 +40,50 @@ tap.test("proxy extended owner", childTest => {
|
|||||||
)
|
)
|
||||||
childTest.end()
|
childTest.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
tap.test("check host validation", async childTest => {
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?width=320&url=https%3A%2F%2Fscontent-syd2-1.cdninstagram.com%2Fv%2Ft51.2885-15%2Fe35%2Fc0.180.1440.1440a%2Fs320x320%2F89848538_212928513111869_8518822308890932076_n.jpg%3F_nc_ht%3Dscontent-syd2-1.cdninstagram.com%26_nc_cat%3D105%26_nc_ohc%3DjPxGnFMF_ZoAX_Q_-jf%26oh%3D018fd6d752e15dedbdf132e004356c98%26oe%3D5F178951")
|
||||||
|
childTest.equal(verifyURL(url).status, "ok", "real cdninstagram syd region")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?url=https%3A%2F%2Fscontent-amt2-1.cdninstagram.com%2Fv%2Ft51.2885-15%2Fe35%2Fp1080x1080%2F101427269_544579909564468_979862184432362192_n.jpg%3F_nc_ht%3Dscontent-amt2-1.cdninstagram.com%26_nc_cat%3D1%26_nc_ohc%3DF-j-3JXkOVgAX_MMJHb%26oh%3De9e0d4ab65a53c15926349bceea9f09d%26oe%3D5F14C4F3")
|
||||||
|
childTest.equal(verifyURL(url).status, "ok", "real cdninstagram amt region")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?url=https%3A%2F%2Fscontent-frx5-1.cdninstagram.com%2Fv%2Ft51.2885-19%2Fs150x150%2F29090066_159271188110124_1152068159029641216_n.jpg%3F_nc_ht%3Dscontent-frx5-1.cdninstagram.com%26_nc_ohc%3DDC7QZiTfNtsAX_ZN33H%26oh%3D77fb5103f058121f7afac07e8e11af44%26oe%3D5F153193")
|
||||||
|
childTest.equal(verifyURL(url).status, "ok", "real cdninstagram frx region")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?url=wow im cool")
|
||||||
|
childTest.same(
|
||||||
|
verifyURL(url),
|
||||||
|
{
|
||||||
|
status: "fail",
|
||||||
|
value: [400, "`url` query parameter is not a valid URL"],
|
||||||
|
},
|
||||||
|
"invalid url"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?url=http%3A%2F%2Fscontent-frx5-1.cdninstagram.com%2Fv%2Ft51.2885-19%2Fs150x150%2F29090066_159271188110124_1152068159029641216_n.jpg%3F_nc_ht%3Dscontent-frx5-1.cdninstagram.com%26_nc_ohc%3DDC7QZiTfNtsAX_ZN33H%26oh%3D77fb5103f058121f7afac07e8e11af44%26oe%3D5F153193")
|
||||||
|
childTest.same(
|
||||||
|
verifyURL(url),
|
||||||
|
{
|
||||||
|
status: "fail",
|
||||||
|
value: [400, "URL protocol must be `https:`"]
|
||||||
|
},
|
||||||
|
"http protocol"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const url = new URL("https://instance.tld/imageproxy?url=https%3A%2F%2Fnotcdninstagram.com")
|
||||||
|
childTest.same(
|
||||||
|
verifyURL(url),
|
||||||
|
{
|
||||||
|
status: "fail",
|
||||||
|
value: [400, "URL host is not allowed"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user