Skip to content

Commit 61f9d2d

Browse files
committed
Use separate cache for path traversal check
We have to check every user input strings, we can reduce the amount of strings with a separate cache specifically for file system checks. The string needs to include at least a slash. Otherwise it can be never be abused. Something we could apply to other vulnerability checks :)
1 parent afdcb85 commit 61f9d2d

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

library/agent/Context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type Context = {
2525
subdomains?: string[]; // https://expressjs.com/en/5x/api.html#req.subdomains
2626
markUnsafe?: unknown[];
2727
cache?: ReturnType<typeof extractStringsFromUserInput>;
28+
cachePathTraversal?: ReturnType<typeof extractStringsFromUserInput>;
2829
/**
2930
* Used to store redirects in outgoing http(s) requests that are started by a user-supplied input (hostname and port / url) to prevent SSRF redirect attacks.
3031
*/
@@ -53,6 +54,7 @@ export function updateContext<K extends keyof Context>(
5354

5455
// Clear all the cached user input strings
5556
delete context.cache;
57+
delete context.cachePathTraversal;
5658
}
5759

5860
/**
@@ -94,6 +96,7 @@ export function runWithContext<T>(context: Context, fn: () => T) {
9496
// In tests the context is often passed by reference
9597
// Make sure to clean up the cache before running the function
9698
delete context.cache;
99+
delete context.cachePathTraversal;
97100

98101
// If there's no context yet, we create a new context and run the function with it
99102
return ContextStorage.run(context, fn);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Context } from "../agent/Context";
2+
import { SOURCES } from "../agent/Source";
3+
import { extractStringsFromUserInput } from "./extractStringsFromUserInput";
4+
5+
type ReturnValue = ReturnType<typeof extractStringsFromUserInput>;
6+
7+
export function extractPathTraversalStringsFromUserInputCached(
8+
context: Context
9+
): ReturnValue {
10+
if (context.cachePathTraversal) {
11+
return context.cachePathTraversal;
12+
}
13+
14+
const userStrings: ReturnValue = new Set();
15+
16+
for (const source of SOURCES) {
17+
if (!context[source]) {
18+
continue;
19+
}
20+
21+
for (const item of extractStringsFromUserInput(context[source])) {
22+
if (item.includes("/")) {
23+
userStrings.add(item);
24+
}
25+
}
26+
}
27+
28+
context.cachePathTraversal = userStrings;
29+
30+
return userStrings;
31+
}

library/vulnerabilities/path-traversal/checkContextForPathTraversal.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Context } from "../../agent/Context";
22
import { InterceptorResult } from "../../agent/hooks/InterceptorResult";
33
import { getPathsToPayload } from "../../helpers/attackPath";
4+
import { extractPathTraversalStringsFromUserInputCached } from "../../helpers/extractPathTraversalStringsFromUserInputCached";
45
import { extractStringsFromUserInputCached } from "../../helpers/extractStringsFromUserInputCached";
56
import { getSourceForUserString } from "../../helpers/getSourceForUserString";
67
import { detectPathTraversal } from "./detectPathTraversal";
@@ -26,7 +27,7 @@ export function checkContextForPathTraversal({
2627
return;
2728
}
2829

29-
for (const str of extractStringsFromUserInputCached(context)) {
30+
for (const str of extractPathTraversalStringsFromUserInputCached(context)) {
3031
if (detectPathTraversal(pathString, str, checkPathStart, isUrl)) {
3132
const source = getSourceForUserString(context, str);
3233
if (source) {

0 commit comments

Comments
 (0)