commit
34e8009f08
|
@ -0,0 +1,54 @@
|
||||||
|
import dns from "dns"
|
||||||
|
import net from "net"
|
||||||
|
import env from "../environment"
|
||||||
|
import { promisify } from "util"
|
||||||
|
|
||||||
|
let blackListArray: string[] | undefined
|
||||||
|
const performLookup = promisify(dns.lookup)
|
||||||
|
|
||||||
|
async function lookup(address: string): Promise<string[]> {
|
||||||
|
if (!net.isIP(address)) {
|
||||||
|
// need this for URL parsing simply
|
||||||
|
if (!address.startsWith("http")) {
|
||||||
|
address = `https://${address}`
|
||||||
|
}
|
||||||
|
address = new URL(address).hostname
|
||||||
|
}
|
||||||
|
const addresses = await performLookup(address, {
|
||||||
|
all: true,
|
||||||
|
})
|
||||||
|
return addresses.map(addr => addr.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function refreshBlacklist() {
|
||||||
|
const blacklist = env.BLACKLIST_IPS
|
||||||
|
const list = blacklist?.split(",") || []
|
||||||
|
let final: string[] = []
|
||||||
|
for (let addr of list) {
|
||||||
|
const trimmed = addr.trim()
|
||||||
|
if (!net.isIP(trimmed)) {
|
||||||
|
const addresses = await lookup(trimmed)
|
||||||
|
final = final.concat(addresses)
|
||||||
|
} else {
|
||||||
|
final.push(trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blackListArray = final
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isBlacklisted(address: string): Promise<boolean> {
|
||||||
|
if (!blackListArray) {
|
||||||
|
await refreshBlacklist()
|
||||||
|
}
|
||||||
|
if (blackListArray?.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// no need for DNS
|
||||||
|
let ips: string[]
|
||||||
|
if (!net.isIP(address)) {
|
||||||
|
ips = await lookup(address)
|
||||||
|
} else {
|
||||||
|
ips = [address]
|
||||||
|
}
|
||||||
|
return !!blackListArray?.find(addr => ips.includes(addr))
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./blacklist"
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { refreshBlacklist, isBlacklisted } from ".."
|
||||||
|
import env from "../../environment"
|
||||||
|
|
||||||
|
describe("blacklist", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
env._set(
|
||||||
|
"BLACKLIST_IPS",
|
||||||
|
"www.google.com,192.168.1.1, 1.1.1.1,2.2.2.2/something"
|
||||||
|
)
|
||||||
|
await refreshBlacklist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should blacklist 192.168.1.1", async () => {
|
||||||
|
expect(await isBlacklisted("192.168.1.1")).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should allow 192.168.1.2", async () => {
|
||||||
|
expect(await isBlacklisted("192.168.1.2")).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should blacklist www.google.com", async () => {
|
||||||
|
expect(await isBlacklisted("www.google.com")).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle a complex domain", async () => {
|
||||||
|
expect(
|
||||||
|
await isBlacklisted("https://www.google.com/derp/?something=1")
|
||||||
|
).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should allow www.microsoft.com", async () => {
|
||||||
|
expect(await isBlacklisted("www.microsoft.com")).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should blacklist an IP that needed trimming", async () => {
|
||||||
|
expect(await isBlacklisted("1.1.1.1")).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should blacklist 1.1.1.1/something", async () => {
|
||||||
|
expect(await isBlacklisted("1.1.1.1/something")).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should blacklist 2.2.2.2", async () => {
|
||||||
|
expect(await isBlacklisted("2.2.2.2")).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
|
@ -104,6 +104,7 @@ const environment = {
|
||||||
SMTP_PORT: parseInt(process.env.SMTP_PORT || ""),
|
SMTP_PORT: parseInt(process.env.SMTP_PORT || ""),
|
||||||
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
|
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
|
||||||
DISABLE_JWT_WARNING: process.env.DISABLE_JWT_WARNING,
|
DISABLE_JWT_WARNING: process.env.DISABLE_JWT_WARNING,
|
||||||
|
BLACKLIST_IPS: process.env.BLACKLIST_IPS,
|
||||||
/**
|
/**
|
||||||
* Enable to allow an admin user to login using a password.
|
* Enable to allow an admin user to login using a password.
|
||||||
* This can be useful to prevent lockout when configuring SSO.
|
* This can be useful to prevent lockout when configuring SSO.
|
||||||
|
|
|
@ -25,6 +25,7 @@ export * as locks from "./redis/redlockImpl"
|
||||||
export * as utils from "./utils"
|
export * as utils from "./utils"
|
||||||
export * as errors from "./errors"
|
export * as errors from "./errors"
|
||||||
export { default as env } from "./environment"
|
export { default as env } from "./environment"
|
||||||
|
export * as blacklist from "./blacklist"
|
||||||
export { SearchParams } from "./db"
|
export { SearchParams } from "./db"
|
||||||
// Add context to tenancy for backwards compatibility
|
// Add context to tenancy for backwards compatibility
|
||||||
// only do this for external usages to prevent internal
|
// only do this for external usages to prevent internal
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { formatBytes } from "../utilities"
|
||||||
import { performance } from "perf_hooks"
|
import { performance } from "perf_hooks"
|
||||||
import FormData from "form-data"
|
import FormData from "form-data"
|
||||||
import { URLSearchParams } from "url"
|
import { URLSearchParams } from "url"
|
||||||
|
import { blacklist } from "@budibase/backend-core"
|
||||||
|
|
||||||
const BodyTypes = {
|
const BodyTypes = {
|
||||||
NONE: "none",
|
NONE: "none",
|
||||||
|
@ -398,6 +399,9 @@ class RestIntegration implements IntegrationBase {
|
||||||
|
|
||||||
this.startTimeMs = performance.now()
|
this.startTimeMs = performance.now()
|
||||||
const url = this.getUrl(path, queryString, pagination, paginationValues)
|
const url = this.getUrl(path, queryString, pagination, paginationValues)
|
||||||
|
if (await blacklist.isBlacklisted(url)) {
|
||||||
|
throw new Error("Cannot connect to URL.")
|
||||||
|
}
|
||||||
const response = await fetch(url, input)
|
const response = await fetch(url, input)
|
||||||
return await this.parseResponse(response, pagination)
|
return await this.parseResponse(response, pagination)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue