Compare commits

...

3 Commits

Author SHA1 Message Date
chungyau97 9fecbdc158 feat: use generic error message for denied hosts in HTTP node 2025-07-21 20:26:35 +08:00
chungyau97 97db0f7b15 feat: add http deny list in HTTP node 2025-07-21 19:52:51 +08:00
chungyau97 5fbb9985f8 feat: add http deny list env variable 2025-07-21 19:49:15 +08:00
4 changed files with 99 additions and 48 deletions

View File

@ -3,6 +3,8 @@ import axios, { AxiosRequestConfig, Method, ResponseType } from 'axios'
import FormData from 'form-data'
import * as querystring from 'querystring'
import { getCredentialData, getCredentialParam } from '../../../src/utils'
import * as ipaddr from 'ipaddr.js'
import dns from 'dns/promises'
class HTTP_Agentflow implements INode {
label: string
@ -230,6 +232,44 @@ class HTTP_Agentflow implements INode {
]
}
private isDeniedIP(ip: string, denyList: string[]): void {
const parsedIp = ipaddr.parse(ip)
for (const entry of denyList) {
if (entry.includes('/')) {
try {
const [range, _] = entry.split('/')
const parsedRange = ipaddr.parse(range)
if (parsedIp.kind() === parsedRange.kind()) {
if (parsedIp.match(ipaddr.parseCIDR(entry))) {
throw new Error('Access to this host is denied by policy.')
}
}
} catch (error) {
throw new Error(`isDeniedIP: ${error}`)
}
} else if (ip === entry) throw new Error('Access to this host is denied by policy.')
}
}
private async checkDenyList(url: string) {
const httpDenyListString: string | undefined = process.env.HTTP_DENY_LIST
if (!httpDenyListString) return url
const httpDenyList = httpDenyListString.split(',').map((ip) => ip.trim())
const urlObj = new URL(url)
const hostname = urlObj.hostname
if (ipaddr.isValid(hostname)) {
this.isDeniedIP(hostname, httpDenyList)
} else {
const addresses = await dns.lookup(hostname, { all: true })
for (const address of addresses) {
this.isDeniedIP(address.address, httpDenyList)
}
}
}
async run(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const method = nodeData.inputs?.method as 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
const url = nodeData.inputs?.url as string
@ -292,6 +332,8 @@ class HTTP_Agentflow implements INode {
// Build final URL with query parameters
const finalUrl = queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url
await this.checkDenyList(finalUrl)
// Prepare request config
const requestConfig: AxiosRequestConfig = {
method: method as Method,

View File

@ -98,6 +98,7 @@
"graphql": "^16.6.0",
"html-to-text": "^9.0.5",
"ioredis": "^5.3.2",
"ipaddr.js": "^2.2.0",
"jsdom": "^22.1.0",
"jsonpointer": "^5.0.1",
"jsonrepair": "^3.11.1",

View File

@ -163,3 +163,10 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200
# REDIS_CA=
# REDIS_KEEP_ALIVE=
# ENABLE_BULLMQ_DASHBOARD=
############################################################################################################
############################################## SECURITY ####################################################
############################################################################################################
# HTTP_DENY_LIST=

File diff suppressed because one or more lines are too long