Skip to content

Commit 9e18931

Browse files
committed
bug detectors: builtin hooks use proper callSiteIDs for hookIDs
- hooks in the path traversal bug detector that need several hookIds now use the callSiteId function
1 parent 1f7e62c commit 9e18931

File tree

3 files changed

+44
-44
lines changed

3 files changed

+44
-44
lines changed

examples/bug-detectors/path-traversal/fuzz.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ const JSZip = require("jszip");
1919
// eslint-disable-next-line @typescript-eslint/no-var-requires
2020
const path = require("path");
2121

22-
// eslint-disable-next-line @typescript-eslint/no-var-requires
23-
const { FuzzedDataProvider } = require("@jazzer.js/core");
24-
2522
/**
2623
* This demonstrates the path traversal bug detector on a vulnerable version of jszip.
2724
* @param { Buffer } data

packages/bug-detectors/internal/path-traversal.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { reportFinding } from "../findings";
1818
import { guideTowardsContainment } from "@jazzer.js/fuzzer";
19-
import { registerBeforeHook } from "@jazzer.js/hooking";
19+
import { callSiteId, registerBeforeHook } from "@jazzer.js/hooking";
2020

2121
/**
2222
* Importing this file adds "before-hooks" for all functions in the built-in `fs`, `fs/promises`, and `path` module and guides
@@ -170,29 +170,32 @@ const functionsWithTwoTargets = [
170170

171171
for (const module of functionsWithTwoTargets) {
172172
for (const functionName of module.functionNames) {
173-
const beforeHook = (
174-
thisPtr: unknown,
175-
params: unknown[],
176-
hookId: number
177-
) => {
178-
if (params === undefined || params.length < 2) {
179-
return;
180-
}
181-
// The first two arguments are paths.
182-
const firstArgument = params[0] as string;
183-
const secondArgument = params[1] as string;
184-
if (firstArgument.includes(goal) || secondArgument.includes(goal)) {
185-
reportFinding(
186-
`Path Traversal in ${functionName}(): called with '${firstArgument}'` +
187-
` and '${secondArgument}'`
188-
);
189-
}
190-
guideTowardsContainment(firstArgument, goal, hookId);
191-
// We don't want to confuse the fuzzer with the same hookId (used as a program counter (PC)),
192-
// so we increment it.
193-
guideTowardsContainment(secondArgument, goal, hookId + 1);
173+
const makeBeforeHook = (extraHookId: number) => {
174+
return (thisPtr: unknown, params: unknown[], hookId: number) => {
175+
if (params === undefined || params.length < 2) {
176+
return;
177+
}
178+
// The first two arguments are paths.
179+
const firstArgument = params[0] as string;
180+
const secondArgument = params[1] as string;
181+
if (firstArgument.includes(goal) || secondArgument.includes(goal)) {
182+
reportFinding(
183+
`Path Traversal in ${functionName}(): called with '${firstArgument}'` +
184+
` and '${secondArgument}'`
185+
);
186+
}
187+
guideTowardsContainment(firstArgument, goal, hookId);
188+
// We don't want to confuse the fuzzer guidance with the same hookId for both function arguments.
189+
// Therefore, we use an extra hookId for the second argument.
190+
guideTowardsContainment(secondArgument, goal, extraHookId);
191+
};
194192
};
195193

196-
registerBeforeHook(functionName, module.moduleName, false, beforeHook);
194+
registerBeforeHook(
195+
functionName,
196+
module.moduleName,
197+
false,
198+
makeBeforeHook(callSiteId(functionName, module.moduleName, "secondId"))
199+
);
197200
}
198201
}

packages/hooking/manager.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,41 +161,41 @@ export class HookManager {
161161
const hook = this._hooks[id];
162162
switch (hook.type) {
163163
case HookType.Before:
164-
(hook.hookFunction as BeforeHookFn)(thisPtr, params, this.callSiteId());
164+
(hook.hookFunction as BeforeHookFn)(thisPtr, params, callSiteId());
165165
break;
166166
case HookType.Replace:
167167
return (hook.hookFunction as ReplaceHookFn)(
168168
thisPtr,
169169
params,
170-
this.callSiteId(),
170+
callSiteId(),
171171
// eslint-disable-next-line @typescript-eslint/ban-types
172172
resultOrOriginalFunction as Function
173173
);
174174
case HookType.After:
175175
(hook.hookFunction as AfterHookFn)(
176176
thisPtr,
177177
params,
178-
this.callSiteId(),
178+
callSiteId(),
179179
resultOrOriginalFunction
180180
);
181181
}
182182
}
183+
}
183184

184-
private callSiteId(): number {
185-
const stackTrace = new Error().stack;
186-
if (!stackTrace || stackTrace.length === 0) {
187-
return 0;
188-
}
189-
let hash = 0,
190-
i,
191-
chr;
192-
for (i = 0; i < stackTrace.length; i++) {
193-
chr = stackTrace.charCodeAt(i);
194-
hash = (hash << 5) - hash + chr;
195-
hash |= 0; // Convert to 32bit integer
196-
}
197-
return hash;
185+
export function callSiteId(...additionalArguments: unknown[]): number {
186+
const stackTrace = additionalArguments?.join(",") + new Error().stack;
187+
if (!stackTrace || stackTrace.length === 0) {
188+
return 0;
189+
}
190+
let hash = 0,
191+
i,
192+
chr;
193+
for (i = 0; i < stackTrace.length; i++) {
194+
chr = stackTrace.charCodeAt(i);
195+
hash = (hash << 5) - hash + chr;
196+
hash |= 0; // Convert to 32bit integer
198197
}
198+
return hash;
199199
}
200200

201201
export const hookManager = new HookManager();
@@ -234,7 +234,7 @@ export function registerAfterHook(
234234
export async function hookBuiltInFunction(hook: Hook): Promise<void> {
235235
const { default: module } = await import(hook.pkg);
236236
const originalFn = module[hook.target];
237-
const id = hookManager.hookIndex(hook);
237+
const id = callSiteId(hookManager.hookIndex(hook), hook.pkg, hook.target);
238238
if (hook.type == HookType.Before) {
239239
module[hook.target] = (...args: unknown[]) => {
240240
(hook.hookFunction as BeforeHookFn)(null, args, id);

0 commit comments

Comments
 (0)