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_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
|
||||
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.
|
||||
* 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 errors from "./errors"
|
||||
export { default as env } from "./environment"
|
||||
export * as blacklist from "./blacklist"
|
||||
export { SearchParams } from "./db"
|
||||
// Add context to tenancy for backwards compatibility
|
||||
// only do this for external usages to prevent internal
|
||||
|
|
|
@ -19,6 +19,7 @@ import { formatBytes } from "../utilities"
|
|||
import { performance } from "perf_hooks"
|
||||
import FormData from "form-data"
|
||||
import { URLSearchParams } from "url"
|
||||
import { blacklist } from "@budibase/backend-core"
|
||||
|
||||
const BodyTypes = {
|
||||
NONE: "none",
|
||||
|
@ -398,6 +399,9 @@ class RestIntegration implements IntegrationBase {
|
|||
|
||||
this.startTimeMs = performance.now()
|
||||
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)
|
||||
return await this.parseResponse(response, pagination)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue