Skip to content

Commit

Permalink
feat: add hostnameExceptionList for ssrf (#100)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced `hostnameExceptionList` for specifying allowed hostnames
within IP blacklists, enhancing SSRF protection.

- **Bug Fixes**
- Adjusted access method for `refererWhiteList` to improve security
configuration handling.

- **Tests**
- Added test cases to validate the functionality of the new
`hostnameExceptionList` feature.

- **Documentation**
- Updated README to include information about the new
`hostnameExceptionList` in security configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
killagu and coderabbitai[bot] authored Jul 8, 2024
1 parent b4d8baa commit 92a34f3
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ In a [Server-Side Request Forgery (SSRF)](https://www.owasp.org/index.php/Server

- ipBlackList(Array) - specific which IP addresses are illegal when requested with `safeCurl`.
- ipExceptionList(Array) - specific which IP addresses are legal within ipBlackList.
hostnameExceptionList(Array) - specifies which hostnames are legal within ipBlackList.
- checkAddress(Function) - determine the ip by the function's return value, `false` means illegal ip.

```js
Expand All @@ -540,6 +541,10 @@ exports.security = {
'10.1.1.1',
'10.10.0.1/24',
],
// legal hostname
hostnameExceptionList: [
'example.com',
],
// checkAddress has higher priority than ipBlackList
checkAddress(ip) {
return ip !== '127.0.0.1';
Expand Down
1 change: 1 addition & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ module.exports = () => {
ssrf: {
ipBlackList: null,
ipExceptionList: null,
hostnameExceptionList: null,
checkAddress: null,
},
};
Expand Down
9 changes: 8 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ exports.preprocessConfig = function(config) {
if (ssrf && ssrf.ipBlackList && !ssrf.checkAddress) {
const containsList = ssrf.ipBlackList.map(getContains);
const exceptionList = (ssrf.ipExceptionList || []).map(getContains);
ssrf.checkAddress = ipAddresses => {
const hostnameExceptionList = ssrf.hostnameExceptionList;
ssrf.checkAddress = (ipAddresses, family, hostname) => {
// Check hostname first
if (hostname && hostnameExceptionList) {
if (hostnameExceptionList.includes(hostname)) {
return true;
}
}
// ipAddresses will be array address on Node.js >= 20
// [
// { address: '220.181.125.241', family: 4 },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

exports.security = {
ssrf: {
ipBlackList: [
'10.0.0.0/8',
'127.0.0.1',
'0.0.0.0/32',
],
hostnameExceptionList: [
'registry.npmjs.org',
'registry.npmmirror.com',
],
},
};
3 changes: 3 additions & 0 deletions test/fixtures/apps/ssrf-hostname-exception-list/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "ssrf-ip-black-list"
}
31 changes: 31 additions & 0 deletions test/ssrf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,37 @@ describe('test/ssrf.test.js', () => {
assert(count === 3);
});
});

describe('hostnameExceptionList', () => {
before(() => {
app = mm.app({ baseDir: 'apps/ssrf-hostname-exception-list' });
return app.ready();
});

it('should safeCurl work', async () => {
const ctx = app.createAnonymousContext();
const host = process.env.CI ? 'registry.npmjs.org' : 'registry.npmmirror.com';
const url = `https://${host}`;
let count = 0;

mm(app, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});
mm(app.agent, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});
mm(ctx, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});

await app.safeCurl(url, { dataType: 'json' });
await app.agent.safeCurl(url, { dataType: 'json' });
await ctx.safeCurl(url, { dataType: 'json' });
});
});
});

async function checkIllegalAddressError(instance, url) {
Expand Down

0 comments on commit 92a34f3

Please sign in to comment.