Second review round found two blocking defects, both verified against a stub
proxy rather than inferred.
HttpsProxyAgent was used for every target, and it always issues CONNECT:
PROXY RECEIVED: "CONNECT example.com:80 HTTP/1.1"
Squid's stock ACL permits CONNECT only to 443, so every http:// monitor would
have broken the moment HTTP_PROXY was set — in exactly the hardened networks
this feature targets. Plain-http targets now use http-proxy-agent, which
sends an absolute-URI request. The agent cache keys on target scheme.
A refused CONNECT is replayed onto the socket, so the proxy's own status was
parsed as the target's: a proxy ACL denial reported "the site returned 403",
and a monitor whose customUpCodes included 403 or 407 was marked UP while the
target was never reached. Proxy-tunnel rejections are now detected via the
agent's proxyConnect event and reported as a network error naming the proxy.
The listener is per request and removed in a finally block, so a failure
cannot be attributed to a concurrent check.
Also from review:
- The fallback for an invalid PROXY_MONITOR_TYPES no longer fires when every
entry was a deliberate but unproxyable type ("ping,port"). Falling back
there would have proxied http and pagespeed, which the operator never
nominated. Typos still fall back; unproxyable lists stay empty and warn.
- Dropped the now-redundant strict/insecure agent split: TLS is applied per
request, and Node already separates socket pools by TLS options, so the
split only doubled tunnels to the proxy.
- Renamed to ProxyPolicy.ts to match the PascalCase peers in the directory.
- The warning no longer echoes the raw config value.
Adds 9 tests covering scheme selection, tunnel rejection, and the narrowed
fallback. Verified by reverting: the tunnel-failure test fails without the fix.
114 lines
3.4 KiB
JSON
Executable File
114 lines
3.4 KiB
JSON
Executable File
{
|
|
"name": "server",
|
|
"version": "1.0.0",
|
|
"description": "",
|
|
"main": "index.js",
|
|
"type": "module",
|
|
"scripts": {
|
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
"test:unit": "NODE_OPTIONS=--experimental-vm-modules jest --coverage --collectCoverageFrom='src/**/*.ts' test/unit",
|
|
"test:unit:services": "NODE_OPTIONS=--experimental-vm-modules jest --coverage --collectCoverageFrom='src/service/**/*.ts' test/unit/services test/unit/providers/network test/unit/providers/notifications",
|
|
"test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --no-coverage test/integration",
|
|
"start": "node --watch ./dist/index.js",
|
|
"dev": "nodemon --exec tsx src/index.js",
|
|
"worker": "QUEUE_MODE=worker node ./dist/index.js",
|
|
"worker:dev": "QUEUE_MODE=worker nodemon --exec tsx src/index.js",
|
|
"clean": "rm -rf dist",
|
|
"build": "npm run clean && npm run build:openapi && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/templates dist/templates",
|
|
"typeCheck": "tsc --noEmit",
|
|
"build:openapi": "tsc --noEmit && tsx scripts/buildOpenApi.ts",
|
|
"lint": "eslint .",
|
|
"lint-fix": "eslint --fix .",
|
|
"format": "prettier --write .",
|
|
"format-check": "prettier --check ."
|
|
},
|
|
"lint-staged": {
|
|
"**/*": "prettier --write --ignore-unknown"
|
|
},
|
|
"keywords": [],
|
|
"author": "",
|
|
"license": "ISC",
|
|
"engines": {
|
|
"node": ">=20"
|
|
},
|
|
"dependencies": {
|
|
"@csstools/css-tokenizer": "^3.0.4",
|
|
"@grpc/grpc-js": "^1.14.3",
|
|
"@grpc/proto-loader": "^0.8.0",
|
|
"axios": "^1.15.2",
|
|
"bcryptjs": "3.0.2",
|
|
"cacheable-lookup": "7.0.0",
|
|
"compression": "1.8.1",
|
|
"cookie-parser": "^1.4.7",
|
|
"cors": "^2.8.5",
|
|
"dockerode": "5.0.0",
|
|
"dotenv": "^16.4.5",
|
|
"express": "^4.19.2",
|
|
"express-rate-limit": "^8.5.1",
|
|
"gamedig": "5.2.0",
|
|
"got": "14.4.7",
|
|
"handlebars": "^4.7.8",
|
|
"helmet": "^8.0.0",
|
|
"http-proxy-agent": "^9.1.0",
|
|
"https-proxy-agent": "^9.1.0",
|
|
"isomorphic-dompurify": "^2.26.0",
|
|
"jmespath": "^0.16.0",
|
|
"jsdom": "^26.1.0",
|
|
"jsonwebtoken": "9.0.2",
|
|
"mjml": "^5.0.0-alpha.4",
|
|
"mongoose": "^8.3.3",
|
|
"multer": "^1.4.5-lts.2",
|
|
"nodemailer": "9.0.5",
|
|
"ping": "0.4.4",
|
|
"proxy-from-env": "^2.1.0",
|
|
"re2": "^1.24.1",
|
|
"sharp": "0.35.3",
|
|
"ssl-checker": "2.0.10",
|
|
"swagger-ui-express": "5.0.1",
|
|
"tldts": "^7.4.10",
|
|
"whoiser": "^1.18.0",
|
|
"winston": "^3.13.0",
|
|
"ws": "^8.19.0",
|
|
"zod": "^4.3.6"
|
|
},
|
|
"devDependencies": {
|
|
"@asteasolutions/zod-to-openapi": "8.5.0",
|
|
"@eslint/js": "^9.17.0",
|
|
"@types/compression": "1.8.1",
|
|
"@types/cookie-parser": "1.4.10",
|
|
"@types/cors": "2.8.19",
|
|
"@types/dockerode": "^4.0.0",
|
|
"@types/express": "5.0.3",
|
|
"@types/gamedig": "^5.0.3",
|
|
"@types/jest": "^30.0.0",
|
|
"@types/jmespath": "^0.15.2",
|
|
"@types/jsdom": "^27.0.0",
|
|
"@types/jsonwebtoken": "9.0.10",
|
|
"@types/mjml": "^4.7.4",
|
|
"@types/multer": "^2.0.0",
|
|
"@types/nodemailer": "8.0.1",
|
|
"@types/papaparse": "^5.5.2",
|
|
"@types/ping": "0.4.4",
|
|
"@types/swagger-ui-express": "4.1.8",
|
|
"@types/ws": "^8.18.1",
|
|
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
"@typescript-eslint/parser": "^8.56.1",
|
|
"eslint": "^9.17.0",
|
|
"eslint-plugin-mocha": "^10.5.0",
|
|
"esm": "3.2.25",
|
|
"globals": "^15.14.0",
|
|
"jest": "^30.2.0",
|
|
"lint-staged": "^16.2.7",
|
|
"mongodb-memory-server": "11.2.0",
|
|
"nodemon": "^3.1.11",
|
|
"prettier": "^3.3.3",
|
|
"prettier-plugin-ini": "1.3.0",
|
|
"ts-jest": "^29.4.6",
|
|
"ts-node": "^10.9.2",
|
|
"tsc-alias": "1.8.16",
|
|
"tsx": "4.20.5",
|
|
"typescript": "5.9.2",
|
|
"typescript-eslint": "^8.56.1"
|
|
}
|
|
}
|