diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 284db30..f3ca9ec 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -12,6 +12,6 @@ jobs:
uses: node-modules/github-actions/.github/workflows/node-test.yml@master
with:
os: 'ubuntu-latest, macos-latest, windows-latest'
- version: '14, 16, 18, 20, 22'
+ version: '14.20.0, 14, 16, 18, 20, 22'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml
new file mode 100644
index 0000000..bac3fac
--- /dev/null
+++ b/.github/workflows/pkg.pr.new.yml
@@ -0,0 +1,23 @@
+name: Publish Any Commit
+on: [push, pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - run: corepack enable
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Build
+ run: npm run prepublishOnly --if-present
+
+ - run: npx pkg-pr-new publish
diff --git a/README.md b/README.md
index 28a36d1..9c63127 100644
--- a/README.md
+++ b/README.md
@@ -551,19 +551,12 @@ exports.security = {
- Forbid `trace` `track` http methods.
-
-
-## Contributors
-
-|[
dead-horse](https://github.com/dead-horse)
|[
fengmk2](https://github.com/fengmk2)
|[
jtyjty99999](https://github.com/jtyjty99999)
|[
popomore](https://github.com/popomore)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
|[
whxaxes](https://github.com/whxaxes)
|
-| :---: | :---: | :---: | :---: | :---: | :---: |
-|[
atian25](https://github.com/atian25)
|[
ai](https://github.com/ai)
|[
Anemone95](https://github.com/Anemone95)
|[
guoshencheng](https://github.com/guoshencheng)
|[
p0sec](https://github.com/p0sec)
|[
pusongyang](https://github.com/pusongyang)
|
-[
ShadyZOZ](https://github.com/ShadyZOZ)
|[
viko16](https://github.com/viko16)
|[
brizer](https://github.com/brizer)
|[
damujiangr](https://github.com/damujiangr)
|[
EliYao](https://github.com/EliYao)
+## License
-This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Wed May 10 2023 16:36:13 GMT+0800`.
+[MIT](https://github.com/eggjs/egg-security/blob/master/LICENSE)
-
+## Contributors
-## License
+[![Contributors](https://contrib.rocks/image?repo=eggjs/egg-security)](https://github.com/eggjs/egg-security/graphs/contributors)
-[MIT](https://github.com/eggjs/egg-security/blob/master/LICENSE)
+Made with [contributors-img](https://contrib.rocks).
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 2188640..6f88144 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -426,19 +426,12 @@ console.log(cmd);
* crossdomain.xml robots.txt 支持,默认都不加,系统可自行加,需要咨询项目安全工程师
* 禁止 trace track 两种类型请求
-
+## License
-## Contributors
-
-|[
dead-horse](https://github.com/dead-horse)
|[
fengmk2](https://github.com/fengmk2)
|[
jtyjty99999](https://github.com/jtyjty99999)
|[
popomore](https://github.com/popomore)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
|[
whxaxes](https://github.com/whxaxes)
|
-| :---: | :---: | :---: | :---: | :---: | :---: |
-|[
atian25](https://github.com/atian25)
|[
ai](https://github.com/ai)
|[
Anemone95](https://github.com/Anemone95)
|[
guoshencheng](https://github.com/guoshencheng)
|[
p0sec](https://github.com/p0sec)
|[
pusongyang](https://github.com/pusongyang)
|
-[
ShadyZOZ](https://github.com/ShadyZOZ)
|[
viko16](https://github.com/viko16)
|[
brizer](https://github.com/brizer)
|[
damujiangr](https://github.com/damujiangr)
|[
EliYao](https://github.com/EliYao)
-
-This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Wed May 10 2023 16:36:13 GMT+0800`.
+[MIT](https://github.com/eggjs/egg-security/blob/master/LICENSE)¬
-
+## Contributors
-## License¬
+[![Contributors](https://contrib.rocks/image?repo=eggjs/egg-security)](https://github.com/eggjs/egg-security/graphs/contributors)
-[MIT](https://github.com/eggjs/egg-security/blob/master/LICENSE)¬
+Made with [contributors-img](https://contrib.rocks).
diff --git a/app/extend/agent.js b/app/extend/agent.js
index a9b7855..6207e09 100644
--- a/app/extend/agent.js
+++ b/app/extend/agent.js
@@ -1,7 +1,5 @@
-'use strict';
-
-const safeCurl = require('../../lib/extend/safe_curl');
+const { safeCurlForApplication } = require('../../lib/extend/safe_curl');
module.exports = {
- safeCurl,
+ safeCurl: safeCurlForApplication,
};
diff --git a/app/extend/application.js b/app/extend/application.js
index bf82e73..5f5d542 100644
--- a/app/extend/application.js
+++ b/app/extend/application.js
@@ -1,6 +1,4 @@
-'use strict';
-
-const safeCurl = require('../../lib/extend/safe_curl');
+const { safeCurlForApplication } = require('../../lib/extend/safe_curl');
const INPUT_CSRF = '\r\n';
@@ -33,4 +31,4 @@ exports.injectHijackingDefense = function injectHijackingDefense(tmplStr) {
return INJECTION_DEFENSE + tmplStr + INJECTION_DEFENSE;
};
-exports.safeCurl = safeCurl;
+exports.safeCurl = safeCurlForApplication;
diff --git a/app/extend/context.js b/app/extend/context.js
index a3f7520..4c8e255 100644
--- a/app/extend/context.js
+++ b/app/extend/context.js
@@ -3,7 +3,7 @@
const debug = require('node:util').debuglog('egg-security:context');
const { nanoid } = require('nanoid/non-secure');
const Tokens = require('csrf');
-const safeCurl = require('../../lib/extend/safe_curl');
+const { safeCurlForContext } = require('../../lib/extend/safe_curl');
const utils = require('../../lib/utils');
const tokens = new Tokens();
@@ -228,5 +228,5 @@ module.exports = {
}
},
- safeCurl,
+ safeCurl: safeCurlForContext,
};
diff --git a/lib/extend/safe_curl.js b/lib/extend/safe_curl.js
index b033deb..7212810 100644
--- a/lib/extend/safe_curl.js
+++ b/lib/extend/safe_curl.js
@@ -1,4 +1,4 @@
-'use strict';
+const SSRF_HTTPCLIENT = Symbol('SSRF_HTTPCLIENT');
/**
* safe curl with ssrf protect
@@ -6,13 +6,28 @@
* @param {Object} options request options
* @return {Promise} response
*/
-module.exports = function safeCurl(url, options = {}) {
- const config = this.config || this.app.config;
- if (config.security.ssrf && config.security.ssrf.checkAddress) {
- options.checkAddress = config.security.ssrf.checkAddress;
+exports.safeCurlForApplication = function safeCurlForApplication(url, options = {}) {
+ const app = this;
+ const ssrfConfig = app.config.security.ssrf;
+ if (ssrfConfig?.checkAddress) {
+ options.checkAddress = ssrfConfig.checkAddress;
} else {
- this.logger.warn('[egg-security] please configure `config.security.ssrf` first');
+ app.logger.warn('[egg-security] please configure `config.security.ssrf` first');
}
- return this.curl(url, options);
+ if (app.config.httpclient.useHttpClientNext && ssrfConfig?.checkAddress) {
+ // use the new httpClient init with checkAddress
+ if (!app[SSRF_HTTPCLIENT]) {
+ app[SSRF_HTTPCLIENT] = app.createHttpClient({
+ checkAddress: ssrfConfig.checkAddress,
+ });
+ }
+ return app[SSRF_HTTPCLIENT].request(url, options);
+ }
+
+ return app.curl(url, options);
+};
+
+exports.safeCurlForContext = function safeCurlForContext(url, options = {}) {
+ return this.app.safeCurl(url, options);
};
diff --git a/package.json b/package.json
index a230493..fa6e008 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,9 @@
{
"name": "egg-security",
"version": "3.3.1",
+ "engines": {
+ "node": ">=14.20.0"
+ },
"description": "security plugin in egg framework",
"eggPlugin": {
"name": "security",
@@ -33,26 +36,21 @@
"devDependencies": {
"beautify-benchmark": "^0.2.4",
"benchmark": "^2.1.4",
- "egg": "^3.15.0",
+ "egg": "^3.26.0",
"egg-bin": "^6.4.0",
"egg-mock": "^5.10.6",
"egg-view-nunjucks": "^2.3.0",
"eslint": "^8.40.0",
"eslint-config-egg": "^12.2.1",
- "git-contributor": "*",
"spy": "^1.0.0",
"supertest": "^6.3.3"
},
- "engines": {
- "node": ">=14.17.0"
- },
"scripts": {
"lint": "eslint .",
"test": "npm run lint -- --fix && npm run test-local",
"test-local": "egg-bin test",
"cov": "egg-bin cov",
- "ci": "npm run lint && npm run cov",
- "contributor": "git-contributor"
+ "ci": "npm run lint && npm run cov"
},
"repository": {
"type": "git",
diff --git a/test/fixtures/apps/ssrf-check-address-useHttpClientNext/config/config.default.js b/test/fixtures/apps/ssrf-check-address-useHttpClientNext/config/config.default.js
new file mode 100644
index 0000000..80d11dd
--- /dev/null
+++ b/test/fixtures/apps/ssrf-check-address-useHttpClientNext/config/config.default.js
@@ -0,0 +1,16 @@
+exports.security = {
+ ssrf: {
+ ipBlackList: [
+ '10.0.0.0/8',
+ '127.0.0.1',
+ '0.0.0.0/32',
+ ],
+ checkAddress(ip) {
+ return ip !== '127.0.0.2';
+ },
+ },
+};
+
+exports.httpclient = {
+ useHttpClientNext: true,
+};
diff --git a/test/fixtures/apps/ssrf-check-address-useHttpClientNext/package.json b/test/fixtures/apps/ssrf-check-address-useHttpClientNext/package.json
new file mode 100644
index 0000000..47b008c
--- /dev/null
+++ b/test/fixtures/apps/ssrf-check-address-useHttpClientNext/package.json
@@ -0,0 +1,3 @@
+{
+ "name": "ssrf-ip-check-address-useHttpClientNext"
+}
diff --git a/test/ssrf.test.js b/test/ssrf.test.js
index 982d5f7..2840731 100644
--- a/test/ssrf.test.js
+++ b/test/ssrf.test.js
@@ -88,6 +88,27 @@ describe('test/ssrf.test.js', () => {
});
});
+ describe('checkAddress with useHttpClientNext = true', () => {
+ before(() => {
+ app = mm.app({ baseDir: 'apps/ssrf-check-address-useHttpClientNext' });
+ return app.ready();
+ });
+
+ it('should safeCurl work', async () => {
+ const urls = [
+ 'https://127.0.0.2/foo',
+ 'https://www.google.com/foo',
+ ];
+ mm.data(dns, 'lookup', '127.0.0.2');
+ const ctx = app.createAnonymousContext();
+ for (const url of urls) {
+ await checkIllegalAddressError(app, url);
+ await checkIllegalAddressError(app.agent, url);
+ await checkIllegalAddressError(ctx, url);
+ }
+ });
+ });
+
describe('ipExceptionList', () => {
before(() => {
app = mm.app({ baseDir: 'apps/ssrf-ip-exception-list' });
@@ -157,6 +178,7 @@ async function checkIllegalAddressError(instance, url) {
await instance.safeCurl(url);
throw new Error('should not execute');
} catch (err) {
- assert(err.name === 'IllegalAddressError');
+ assert.equal(err.name, 'IllegalAddressError');
+ assert.match(err.message, /illegal address/);
}
}