Skip to content

Commit

Permalink
Merge pull request #418 from AikidoSec/patch-allowed-ip
Browse files Browse the repository at this point in the history
If IP in bypass list, don't block or detect
  • Loading branch information
hansott authored Oct 16, 2024
2 parents 24aa569 + e66d6e4 commit f09bfdc
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 19 deletions.
72 changes: 53 additions & 19 deletions library/sinks/Undici.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,31 @@ wrap(dns, "lookup", function lookup(original) {
};
});

const context: Context = {
remoteAddress: "::1",
method: "POST",
url: "http://localhost:4000",
query: {},
headers: {},
body: {
image: "http://localhost:4000/api/internal",
},
cookies: {},
routeParams: {},
source: "express",
route: "/posts/:id",
};
function createContext(): Context {
return {
remoteAddress: "::1",
method: "POST",
url: "http://localhost:4000",
query: {},
headers: {},
body: {
image: "http://localhost:4000/api/internal",
},
cookies: {},
routeParams: {},
source: "express",
route: "/posts/:id",
};
}

let server;
t.before(() => {
server = require("http").createServer((req, res) => {
res.end("Hello, world!");
});
server.unref();
server.listen(4000);
});

t.test(
"it works",
Expand All @@ -62,9 +73,17 @@ t.test(
},
async (t) => {
const logger = new LoggerForTesting();
const api = new ReportingAPIForTesting();
const api = new ReportingAPIForTesting({
success: true,
endpoints: [],
configUpdatedAt: 0,
heartbeatIntervalInMS: 10 * 60 * 1000,
blockedUserIds: [],
allowedIPAddresses: ["1.2.3.4"],
block: true,
receivedAnyStats: false,
});
const agent = new Agent(true, logger, api, new Token("123"), undefined);

agent.start([new Undici()]);

const {
Expand Down Expand Up @@ -151,7 +170,18 @@ t.test(
await t.rejects(() => request("invalid url"));
await t.rejects(() => request({ hostname: "" }));

await runWithContext(context, async () => {
await runWithContext(
{
...createContext(),
remoteAddress: "1.2.3.4",
},
async () => {
// Bypass the block using an allowed IP
await request("http://localhost:4000/api/internal");
}
);

await runWithContext(createContext(), async () => {
await request("https://google.com");

const error0 = await t.rejects(() => request("http://localhost:9876"));
Expand Down Expand Up @@ -226,7 +256,7 @@ t.test(
});

await runWithContext(
{ ...context, routeParams: { param: "http://0" } },
{ ...createContext(), routeParams: { param: "http://0" } },
async () => {
const error = await t.rejects(() => request("http://0"));
if (error instanceof Error) {
Expand All @@ -240,7 +270,7 @@ t.test(

await runWithContext(
{
...context,
...createContext(),
...{
body: {
image2: [
Expand Down Expand Up @@ -286,3 +316,7 @@ t.test(
]);
}
);

t.after(() => {
server.close();
});
10 changes: 10 additions & 0 deletions library/sinks/undici/wrapDispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ export function wrapDispatch(orig: Dispatch, agent: Agent): Dispatch {
* Checks if it's a redirect to a private IP that originates from a user input and blocks it if it is.
*/
function blockRedirectToPrivateIP(url: URL, context: Context, agent: Agent) {
const isAllowedIP =
context &&
context.remoteAddress &&
agent.getConfig().isAllowedIP(context.remoteAddress);

if (isAllowedIP) {
// If the IP address is allowed, we don't need to block the request
return;
}

const found = isRedirectToPrivateIP(url, context);

if (found) {
Expand Down
11 changes: 11 additions & 0 deletions library/vulnerabilities/ssrf/inspectDNSLookupCalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ function wrapDNSLookupCallback(
return callback(err, addresses, family);
}

const isAllowedIP =
context &&
context.remoteAddress &&
agent.getConfig().isAllowedIP(context.remoteAddress);

if (isAllowedIP) {
// If the IP address is allowed, we don't need to block the request
// Just call the original callback to allow the DNS lookup
return callback(err, addresses, family);
}

const libraryRoot = resolve(__dirname, "../..");

// Used to get the stack trace of the calling location
Expand Down

0 comments on commit f09bfdc

Please sign in to comment.