Skip to content

Commit 27ee22d

Browse files
authored
Merge pull request #452 from AikidoSec/ghost-bugfix
Preserve original handler name for Ghost
2 parents 59bb172 + 9147240 commit 27ee22d

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

library/sources/Express.test.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,18 @@ function getApp(userMiddleware = true) {
105105
});
106106
}
107107

108-
// A middleware that is used as a route
109-
app.use("/api/*path", (req, res, next) => {
108+
function apiMiddleware(
109+
req: express.Request,
110+
res: express.Response,
111+
next: express.NextFunction
112+
) {
110113
const context = getContext();
111114

112115
res.send(context);
113-
});
116+
}
117+
118+
// A middleware that is used as a route
119+
app.use("/api/*path", apiMiddleware);
114120

115121
app.get("/", (req, res) => {
116122
const context = getContext();
@@ -523,3 +529,32 @@ t.test("it allows white-listed IP address", async () => {
523529
t.same(res.statusCode, 200);
524530
}
525531
});
532+
533+
t.test("it preserves original function name in Layer object", async () => {
534+
const app = getApp();
535+
536+
/**
537+
* Ghost uses the name of the original function to look up the site router (a middleware)
538+
* Before the fix, the name of the middleware was changed to `<anonymous>` by Zen
539+
*
540+
* _getSiteRouter(req) {
541+
* let siteRouter = null;
542+
*
543+
* req.app._router.stack.every((router) => {
544+
* if (router.name === 'SiteRouter') {
545+
* siteRouter = router;
546+
* return false;
547+
* }
548+
*
549+
* return true;
550+
* });
551+
*
552+
* return siteRouter;
553+
* }
554+
*/
555+
t.same(
556+
// @ts-expect-error stack is private
557+
app.router.stack.filter((stack) => stack.name === "apiMiddleware").length,
558+
1
559+
);
560+
});

library/sources/express/wrapRequestHandler.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function wrapRequestHandler(
99
handler: RequestHandler,
1010
agent: Agent
1111
): RequestHandler {
12-
return (req, res, next) => {
12+
const fn: RequestHandler = (req, res, next) => {
1313
const context = contextFromRequest(req);
1414

1515
return runWithContext(context, () => {
@@ -40,4 +40,36 @@ export function wrapRequestHandler(
4040
return handler(req, res, next);
4141
});
4242
};
43+
44+
if (handler.name) {
45+
preserveFunctionName(fn, handler.name);
46+
}
47+
48+
return fn;
49+
}
50+
51+
/**
52+
* Preserve the original function name
53+
* e.g. Ghost looks up a middleware function by name in the router stack
54+
*
55+
* Object.getOwnPropertyDescriptor(function myFunction() {}, "name")
56+
*
57+
* {
58+
* value: 'myFunction',
59+
* writable: false,
60+
* enumerable: false,
61+
* configurable: true
62+
* }
63+
*/
64+
function preserveFunctionName(wrappedFunction: Function, originalName: string) {
65+
try {
66+
Object.defineProperty(wrappedFunction, "name", {
67+
value: originalName,
68+
writable: false,
69+
enumerable: false,
70+
configurable: true,
71+
});
72+
} catch (e) {
73+
// Ignore
74+
}
4375
}

0 commit comments

Comments
 (0)